import json
import shutil
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Dict, Tuple, Optional
from importlib import resources as importlib_resources

from nemo_library_etl.adapter._utils.config import _deep_merge


def get_config_differences(current: Dict[str, Any], default: Dict[str, Any]) -> Dict[str, Any]:
    """Return only those keys/values from 'current' that differ from 'default' (recursive)."""
    differences: Dict[str, Any] = {}

    for key, value in current.items():
        if key not in default:
            differences[key] = value
            continue

        default_value = default[key]

        if isinstance(value, dict) and isinstance(default_value, dict):
            nested = get_config_differences(value, default_value)
            if nested:
                differences[key] = nested
            continue

        if value != default_value:
            differences[key] = value

    return differences


@dataclass(frozen=True)
class MigManConfigStore:
    package: str = "nemo_library_etl"
    default_resource_rel: str = "adapter/migman/config/default_config_migman.json"
    local_config_file: Path = Path("./config") / "migman.json"

    def load_default(self) -> Dict[str, Any]:
        with importlib_resources.as_file(
            importlib_resources.files(self.package).joinpath(self.default_resource_rel)
        ) as p:
            with p.open("r", encoding="utf-8") as f:
                return json.load(f)

    def load_local(self) -> Dict[str, Any]:
        if self.local_config_file.exists():
            with self.local_config_file.open("r", encoding="utf-8") as f:
                return json.load(f)
        return {}

    def load_merged(self) -> Tuple[Dict[str, Any], Dict[str, Any], Dict[str, Any]]:
        default_config = self.load_default()
        local_config = self.load_local()
        merged_config = _deep_merge(default_config, local_config)
        return default_config, local_config, merged_config

    def save_local(self, merged_config: Dict[str, Any]) -> Tuple[bool, str]:
        """
        Save only non-default values to local config file.
        Returns (success, message).
        """
        try:
            default_config = self.load_default()
            config_to_save = get_config_differences(merged_config, default_config)

            self.local_config_file.parent.mkdir(exist_ok=True)

            if self.local_config_file.exists():
                backup_file = self.local_config_file.with_suffix(".json.bak")
                shutil.copy2(self.local_config_file, backup_file)

            with self.local_config_file.open("w", encoding="utf-8") as f:
                json.dump(config_to_save, f, indent=4, ensure_ascii=False)

            return True, "Configuration saved successfully!"
        except Exception as e:
            return False, f"Error saving configuration: {e}"

    def reset_local(self) -> Tuple[bool, str]:
        """
        Delete local config file (with backup).
        Returns (success, message).
        """
        try:
            if self.local_config_file.exists():
                backup_file = self.local_config_file.with_suffix(".json.reset.bak")
                shutil.copy2(self.local_config_file, backup_file)
                self.local_config_file.unlink()
                return True, f"Configuration reset successfully! Backup saved as {backup_file.name}"
            return True, "No local configuration found to reset."
        except Exception as e:
            return False, f"Error resetting configuration: {e}"