import datetime
import logging

from fastapi import HTTPException
from sqlalchemy.orm import Session

from components.dbo.models.llm_config import LLMConfig as LLMConfigSQL
from schemas.llm_config import LLMConfig as LLMConfigScheme, LLMConfigCreateScheme


logger = logging.getLogger(__name__)


class LLMConfigService:
    """
    Сервис для работы с параметрами LLM.
    """

    def __init__(self, db: Session):
        logger.info("LLMConfigService initializing")
        self.db = db

    
    def create(self, config_scheme: LLMConfigCreateScheme):
        logger.info("Creating a new config")
        with self.db() as session:
            new_config: LLMConfigSQL = LLMConfigSQL(**config_scheme.dict())
            session.add(new_config)
            session.commit()
            session.refresh(new_config)
            
            if(new_config.is_default):
                self.set_as_default(new_config.id)
                
            return LLMConfigScheme(**new_config.to_dict())


    def get_list(self) -> list[LLMConfigScheme]:
        with self.db() as session:
            configs: list[LLMConfigSQL] = session.query(LLMConfigSQL).all()
            
            return [
                LLMConfigScheme(**config.to_dict())
                for config in configs
            ]
    
    def get_by_id(self, id: int) -> LLMConfigScheme:
        with self.db() as session:
            config: LLMConfigSQL = session.query(LLMConfigSQL).filter(LLMConfigSQL.id == id).first()

            if not config:
                raise HTTPException(
                    status_code=400, detail=f"Item with id {id} not found"
                    )

            return LLMConfigScheme(**config.to_dict())


    def get_default(self) -> LLMConfigScheme:
        with self.db() as session:
            config: LLMConfigSQL = session.query(LLMConfigSQL).filter(LLMConfigSQL.is_default).first()

            if not config:
                # Возвращаем дефолтнейшие параметры в случае, если ничего нет. 
                # Неочевидно, но в случае факапа всё работать будет.
                return LLMConfigScheme(
                    is_default=True,
                    model='meta-llama/Llama-3.3-70B-Instruct-Turbo',
                    temperature=0.14,
                    top_p=0.95,
                    min_p=0.05,
                    frequency_penalty=-0.001,
                    presence_penalty=1.3,
                    n_predict=1000,
                    seed=42,
                    id=0,
                    date_created=datetime.datetime.now(datetime.timezone.utc)
                )

            return LLMConfigScheme(**config.to_dict())


    def set_as_default(self, id: int):
        logger.info(f"Set default config: {id}")
        
        with self.db() as session:
            session.query(LLMConfigSQL).filter(LLMConfigSQL.is_default).update({"is_default": False})
            config_new: LLMConfigSQL = session.query(LLMConfigSQL).filter(LLMConfigSQL.id == id).first()

            if not config_new:
                raise HTTPException(
                    status_code=400, detail=f"Item with id {id} not found"
                )

            config_new.is_default = True
            session.commit()


    
    def update(self, id: int, new_config: LLMConfigScheme): 
        logger.info("Updating default config")
        with self.db() as session:
            config: LLMConfigSQL = session.query(LLMConfigSQL).filter(LLMConfigSQL.id == id).first()  

            if not config:
                raise HTTPException(
                    status_code=400, detail=f"Item with id {id} not found"
                    )
        
            update_data = new_config.model_dump(exclude_unset=True)
            
            for key, value in update_data.items():
                if hasattr(config, key):
                    setattr(config, key, value)
            
            
            session.commit()
            
            
            if(new_config.is_default):
                self.set_as_default(new_config.id)
                
            session.refresh(config)
            return config


    def delete(self, id: int): 
        logger.info("Deleting config: {id}")
        with self.db() as session:
            config_to_del: LLMConfigSQL = session.query(LLMConfigSQL).get(id)
            config_default: LLMConfigSQL = session.query(LLMConfigSQL).filter(LLMConfigSQL.is_default).first()  

            if config_to_del.id == config_default.id:
                raise HTTPException(
                    status_code=400, detail=f"The default config cannot be deleted"
                    )
            
            session.delete(config_to_del)
            session.commit()