# -*- coding: utf-8 -*-

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

from ddans.native.system import NSystem


class DConfig:

    def __init__(self,
                 path: str = "config.json",
                 default_config: Optional[Dict] = None):
        self.config_path = Path(path)
        self.default_config = default_config or {}
        self._config: Optional[Dict] = None
        self._loaded = False
        self._modified = False

    def load(self, force: bool = False) -> Dict:
        if self._loaded and not force and self._config is not None:
            return self._config.copy()

        self._config = self.default_config.copy()
        if not self.config_path.exists():
            self._loaded = True
            self._modified = True
            self._save_if_needed()
            return self._config.copy()

        cfg = NSystem.read_json(self.config_path, None)
        if cfg:
            self._config = cfg
            self._loaded = True
            self._modified = False
        else:
            self._loaded = True
            self._modified = True
            self._save_if_needed()
        return self._config.copy()

    def _save_if_needed(self) -> None:
        if self._modified and self._config is not None:
            self.save()

    def save(self, force: bool = False) -> bool:
        if self._config is None:
            return False
        if not self._modified and not force:
            return True

        return NSystem.write_json(self._config, self.config_path)

    def get(self, key: str, default: Any = None) -> Any:
        if not self._loaded or self._config is None:
            self.load()

        # 支持多级键
        keys = key.split('.')
        value = self._config

        for k in keys:
            if isinstance(value, dict) and k in value:
                value = value[k]
            else:
                return default

        return value

    def set(self, key: str, value: Any, save: bool = True) -> bool:
        if not self._loaded or self._config is None:
            self.load()

        # 支持多级键
        keys = key.split('.')
        current = self._config

        # 遍历到最后一层
        for k in keys[:-1]:
            if k not in current or not isinstance(current[k], dict):
                current[k] = {}
            current = current[k]

        old_value = current.get(keys[-1])
        if old_value == value:
            return True

        # 设置值
        current[keys[-1]] = value
        self._modified = True

        # 如果需要立即保存
        if save:
            return self.save()

        return True

    def update(self, updates: Dict, save: bool = False) -> bool:
        # 确保配置已加载
        if not self._loaded or self._config is None:
            self.load()

        # 递归更新
        modified = self._update_dict(self._config, updates)

        if modified:
            self._modified = True

        if save and modified:
            return self.save()

        return True

    def _update_dict(self, target: Dict, updates: Dict) -> bool:
        modified = False

        for key, value in updates.items():
            if (key in target and isinstance(target[key], dict)
                    and isinstance(value, dict)):
                # 递归更新嵌套字典
                if self._update_dict(target[key], value):
                    modified = True
            elif key not in target or target[key] != value:
                # 值不同，更新
                target[key] = value
                modified = True

        return modified

    def delete(self, key: str, save: bool = False) -> bool:
        # 确保配置已加载
        if not self._loaded or self._config is None:
            self.load()

        keys = key.split('.')
        current = self._config

        # 找到要删除的项的父级
        for k in keys[:-1]:
            if k in current and isinstance(current[k], dict):
                current = current[k]
            else:
                return False  # 键不存在

        if keys[-1] in current:
            del current[keys[-1]]
            self._modified = True
            if save:
                return self.save()
            return True
        return False

    def reset(self, save: bool = True) -> bool:
        self._config = self.default_config.copy()
        self._loaded = True
        self._modified = True
        if save:
            return self.save()
        return True

    def reload(self) -> Dict:
        return self.load(force=True)

    def get_all(self) -> Dict:
        if not self._loaded or self._config is None:
            self.load()
        return self._config.copy()

    def exists(self, key: str) -> bool:
        if not self._loaded or self._config is None:
            self.load()

        keys = key.split('.')
        value = self._config

        for k in keys:
            if isinstance(value, dict) and k in value:
                value = value[k]
            else:
                return False
        return True

    def ensure_exists(self,
                      key: str,
                      default_value: Any,
                      save: bool = False) -> Any:
        if not self.exists(key):
            self.set(key, default_value, auto_save=save)
            return default_value

        return self.get(key)

    def is_modified(self) -> bool:
        return self._modified

    def auto_save(self) -> bool:
        if self._modified:
            return self.save()
        return True

    def __getitem__(self, key: str) -> Any:
        return self.get(key)

    def __setitem__(self, key: str, value: Any) -> None:
        self.set(key, value, save=False)

    def __contains__(self, key: str) -> bool:
        return self.exists(key)
