from typing import Any, Dict, Optional
from pathlib import Path
import json


class ConfigManager:
    _instance: Optional["ConfigManager"] = None

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

    def __init__(self):
        if not hasattr(self, "_initialized"):
            self._config: Dict[str, Any] = {}
            self._config_path: Optional[Path] = None
            self._load_defaults()
            self._initialized = True

    def _load_defaults(self) -> None:
        self._config = {
            "orchestration": {
                "max_iterations": 10,
                "timeout": 300.0,
                "verbose": False,
                "retry_attempts": 3,
            },
            "agent": {
                "temperature": 0.7,
                "max_tokens": 4096,
            },
            "logging": {
                "level": "info",
            },
        }

    def load_from_file(self, config_path: str) -> None:
        path = Path(config_path)
        if not path.exists():
            raise FileNotFoundError(f"Config file not found: {config_path}")

        with open(path, "r") as f:
            file_config = json.load(f)

        self._merge_config(file_config)
        self._config_path = path

    def _merge_config(self, new_config: Dict[str, Any]) -> None:
        for key, value in new_config.items():
            if (
                key in self._config
                and isinstance(self._config[key], dict)
                and isinstance(value, dict)
            ):
                self._config[key].update(value)
            else:
                self._config[key] = value

    def get(self, key: str, default: Any = None) -> Any:
        keys = key.split(".")
        value = self._config
        for k in keys:
            if isinstance(value, dict):
                value = value.get(k)
                if value is None:
                    return default
            else:
                return default
        return value

    def set(self, key: str, value: Any) -> None:
        keys = key.split(".")
        config = self._config
        for k in keys[:-1]:
            if k not in config:
                config[k] = {}
            config = config[k]
        config[keys[-1]] = value

    def get_all(self) -> Dict[str, Any]:
        return self._config.copy()

    def save_to_file(self, config_path: Optional[str] = None) -> None:
        path = Path(config_path) if config_path else self._config_path
        if path is None:
            raise ValueError("No config path specified")

        with open(path, "w") as f:
            json.dump(self._config, f, indent=2)


def get_config() -> ConfigManager:
    return ConfigManager()
