import configparser
import os
import threading
from dataclasses import dataclass
from typing import Dict, Any, Optional

from pilot.generater.generation_config import GenerationConfigDTO
from pilot.generater.lmstudioai import lmstudioAISingleton
from pilot.generater.vectorengineai import VectorEngineAISingleton
from pilot.generater.ai_base import AIBase

@dataclass
class AIConfigDTO:
    veraApi: str
    veraModel: str
    verUrl: str
    timeOut: int

class VeraSingleton:
    _instance: Optional['VeraSingleton'] = None
    _lock = threading.Lock()

    def __new__(cls):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super(VeraSingleton, cls).__new__(cls)
                    cls._instance._initialized = False
        return cls._instance

    def __init__(self):
        if not self._initialized:
            with self._lock:

                cwd = os.getcwd()
                filepath = os.path.join(cwd, 'config', 'vera.properties')
                self.config = configparser.ConfigParser()
                self.config.optionxform = str

                with open(filepath, 'r', encoding='utf-8') as f:
                    content = f.read()
                if not content.lstrip().startswith('['):
                    content = '[DEFAULT]\n' + content
                self.config.read_string(content)
                self.config_dto = self.create_ai_dto()
                self.veraApi = self.config_dto.veraApi
                self.model_name = self.config_dto.veraModel
                self.base_url = self.config_dto.verUrl
                self.timeout = self.config_dto.timeOut
                self.ai_instance: AIBase  = self.get_ai_instance()
                self._initialized = True

    def get_ai_instance(self) :
        match self.veraApi:
            case "lmstudio":
                return lmstudioAISingleton.get_instance(model_name=self.model_name,base_url= self.base_url)
            case "vector":
                return VectorEngineAISingleton.get_instance(model_name=self.model_name,base_url= self.base_url)
            case "ollama":
                return AIBase()
            case "gemini":
                return AIBase()
            case "openwebui":
                return AIBase()
            case "gemini":
                return AIBase()
            case "qwen":
                return AIBase()
            case "azure":
                return AIBase()
        return AIBase()

    def generate_content(
            self,
            prompt: str,
            gen_config: Optional[GenerationConfigDTO] = None,
            stream: Optional[bool] = None,
            **kwargs
    ) -> Dict[str, Any]:
        """
        统一的对外调用接口。
        如果外部没有指定 gen_config，将默认使用 properties 文件中读取的 timeout 配置。
        """
        if gen_config is None:
            # 自动应用 vera.properties 中的 timeout 配置
            gen_config = GenerationConfigDTO(timeout=self.timeout)

        # 委托给底层的 ai_instance 执行具体的生成逻辑
        return self.ai_instance.generate_content(
            prompt=prompt,
            gen_config=gen_config,
            stream=stream,
            **kwargs
        )

    @classmethod
    def get_instance(cls) -> 'VeraSingleton':
        return cls()

    def create_ai_dto(self) -> AIConfigDTO:
        vera_api = self.get('DEFAULT', 'vera_api', fallback='.')
        vera_model = self.get('DEFAULT', 'vera_model', fallback='.')
        ver_url = self.get('DEFAULT', 'ver_url', fallback='.')
        time_out = int(self.get('DEFAULT', 'time_out', fallback=600))

        return AIConfigDTO(
            veraApi = vera_api,
            veraModel = vera_model,
            verUrl = ver_url,
            timeOut = time_out
        )

    def get(self, section, option, fallback=None, cast_type=str):
        try:
            if cast_type == bool:
                return self.config.getboolean(section, option)
            elif cast_type == int:
                return self.config.getint(section, option)
            elif cast_type == float:
                return self.config.getfloat(section, option)
            else:
                return self.config.get(section, option)
        except (configparser.NoSectionError, configparser.NoOptionError):
            return fallback

