import configparser
import inspect
import os
from dataclasses import dataclass
from typing import Optional


@dataclass
class ConfigDTO:
    project: str
    log_level: str
    threads: int
    lm_studio_api_url: str
    lm_studio_model_name: str
    work_space: str
    copy_path: str
    json_file_path:str
    steps: list[str]


class ConfigReader:
    _instance = None
    _loaded = False

    def __new__(cls, filepath=None):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self, filepath=None):
        if self._loaded:
            return

        try:
            if filepath is None:
                filepath = self.find_config_path()
            if not os.path.exists(filepath):
                raise FileNotFoundError(f"設定ファイルが見つかりません: {filepath}")

            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._loaded = True
        except Exception as e:
            print(f"設定ファイル読み込みエラー: {e}")
            raise

    @classmethod
    def find_config_path(cls):
        cwd = os.getcwd()
        candidate_path = os.path.join(cwd, 'config', 'control.properties')
        if os.path.exists(candidate_path):
            return candidate_path

        stack = inspect.stack()
        for frame in stack:
            caller_file = frame.filename
            caller_dir = os.path.dirname(os.path.abspath(caller_file))
            possible_path = os.path.abspath(os.path.join(caller_dir, '..', '..', 'config', 'control.properties'))
            if os.path.exists(possible_path):
                return possible_path

        base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
        fallback_path = os.path.join(base_dir, 'config', 'control.properties')

        if os.path.exists(fallback_path):
            return fallback_path

        raise FileNotFoundError("control.properties が期待される場所に見つかりません")

    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

    def get_dto(self) -> ConfigDTO:
        project = self.get('DEFAULT', 'project', fallback='')
        log_level = self.get('DEFAULT', 'log_level', fallback='INFO')
        threads = self.get('DEFAULT', 'threads', fallback=1, cast_type=int)
        lm_studio_api_url = self.get('DEFAULT', 'lm_studio_api_url', fallback='.')
        lm_studio_model_name = self.get('DEFAULT', 'lm_studio_model_name', fallback='.')
        work_space = self.get('DEFAULT', 'work_space', fallback='.')
        copy_path = self.get('DEFAULT', 'copy_file_path', fallback='.')
        json_file_path = self.get('DEFAULT', 'json_file_path', fallback='.')
        steps_str = self.get('DEFAULT', 'steps', fallback='')

        steps = [s.strip() for s in steps_str.split(',')] if steps_str else []

        return ConfigDTO(
            project=project,
            log_level=log_level,
            threads=threads,
            lm_studio_api_url=lm_studio_api_url,
            lm_studio_model_name=lm_studio_model_name,
            work_space=work_space,
            copy_path=copy_path,
            json_file_path=json_file_path,
            steps=steps
        )


# 全局設定管理用の変数
_global_config: Optional[ConfigDTO] = None


def init_config(filepath=None):
    global _global_config
    try:
        config_reader = ConfigReader(filepath)
        _global_config = config_reader.get_dto()
    except Exception as e:
        print(f"設定初期化エラー: {e}")
        raise


def get_config() -> ConfigDTO:
    global _global_config
    if _global_config is None:
        raise RuntimeError("設定が初期化されていません。init_config() を最初に呼び出してください。")
    return _global_config
