from __future__ import annotations

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

from mockmesh.config.resolver import ConfigResolver
from mockmesh.core.logger import get_logger

_DEFAULTS_DIR = Path(__file__).parent.parent / "defaults"

_DEFAULT_FILES = {
    "http":      _DEFAULTS_DIR / "http.json",
    "aws":       _DEFAULTS_DIR / "aws.json",
    "azure":     _DEFAULTS_DIR / "azure.json",
    "gcp":       _DEFAULTS_DIR / "gcp.json",
    "streaming": _DEFAULTS_DIR / "streaming.json",
}

_RULES_SECTIONS = ("http", "aws", "azure", "gcp", "kafka", "rabbitmq")


def _tag(rules: List[dict], tier: str) -> List[dict]:
    return [dict(r, _tier=tier) for r in rules]


class ConfigLoader:
    def __init__(
        self,
        user_config_path:  Optional[str | Path] = None,
        responses_folder:  Optional[Path]        = None,
    ) -> None:
        self.user_config_path  = Path(user_config_path) if user_config_path else None
        self._responses_folder = responses_folder
        self._log = get_logger("config.loader")

    def load(self) -> ConfigResolver:
        default_rules = self._load_defaults()
        folder_rules  = self._load_folder_rules()
        user_rules    = self._load_user() if self.user_config_path else {}

        merged: Dict[str, List[dict]] = {}
        for section in _RULES_SECTIONS:
            u = _tag(user_rules.get(section, []),    "user")
            f = _tag(folder_rules.get(section, []),  "user")
            d = _tag(default_rules.get(section, []), "default")
            merged[section] = u + f + d

        self._log.info(
            "ConfigLoader ready",
            extra={
                "mm_default_sections": list(default_rules.keys()),
                "mm_folder_sections":  list(folder_rules.keys()),
                "mm_user_sections":    list(user_rules.keys()),
                "mm_user_path":        str(self.user_config_path or "none"),
                "mm_responses_folder": str(self._responses_folder or "none"),
            },
        )
        return ConfigResolver(rules=merged)

    def load_flat(self) -> dict:
        merged: dict = {}
        for section, rules in self._load_defaults().items():
            merged[section] = {"rules": list(rules)}
        for section, rules in self._load_folder_rules().items():
            if section in merged:
                merged[section]["rules"] = list(rules) + merged[section]["rules"]
            else:
                merged[section] = {"rules": list(rules)}
        for section, rules in (self._load_user() if self.user_config_path else {}).items():
            if section in merged:
                merged[section]["rules"] = list(rules) + merged[section]["rules"]
            else:
                merged[section] = {"rules": list(rules)}
        return merged

    # ------------------------------------------------------------------

    def _load_defaults(self) -> Dict[str, List[dict]]:
        result: Dict[str, List[dict]] = {}
        for section, path in _DEFAULT_FILES.items():
            if not path.exists():
                continue
            try:
                data = json.loads(path.read_text(encoding="utf-8"))
                if section == "streaming":
                    for sub in ("kafka", "rabbitmq"):
                        sub_data = data.get(sub, {})
                        rules = sub_data.get("rules", [])
                        result[sub] = rules
                        self._log.debug("Loaded default config section",
                                        extra={"mm_section": sub, "mm_rules": len(rules)})
                else:
                    result[section] = data.get("rules", [])
                    self._log.debug("Loaded default config section",
                                    extra={"mm_section": section, "mm_rules": len(result[section])})
            except json.JSONDecodeError as exc:
                self._log.error("Malformed default config – section skipped",
                                extra={"mm_section": section, "mm_error": str(exc)})
        return result

    def _load_folder_rules(self) -> Dict[str, List[dict]]:
        if not self._responses_folder:
            return {}
        result: Dict[str, List[dict]] = {}
        for section in _RULES_SECTIONS:
            p = self._responses_folder / f"{section}.json"
            if not p.is_file():
                continue
            try:
                data  = json.loads(p.read_text(encoding="utf-8"))
                rules = data.get("rules", [])
                if rules:
                    result[section] = rules
                    self._log.debug("Loaded folder config section",
                                    extra={"mm_section": section, "mm_path": str(p),
                                           "mm_rules": len(rules)})
            except json.JSONDecodeError as exc:
                self._log.error("Malformed folder config – section skipped",
                                extra={"mm_section": section, "mm_path": str(p),
                                       "mm_error": str(exc)})
        return result

    def _load_user(self) -> Dict[str, List[dict]]:
        path = self.user_config_path
        if not path or not path.exists():
            self._log.warning("User config not found – falling back to defaults only",
                              extra={"mm_path": str(path)})
            return {}
        try:
            raw = json.loads(path.read_text(encoding="utf-8"))
        except json.JSONDecodeError as exc:
            self._log.error("Malformed user config – falling back to defaults only",
                            extra={"mm_path": str(path), "mm_error": str(exc)})
            return {}
        result: Dict[str, List[dict]] = {}
        for section, data in raw.items():
            if section.startswith("_"):
                continue
            if isinstance(data, dict) and "rules" in data:
                result[section] = data["rules"]
                self._log.debug("Loaded user config section",
                                extra={"mm_section": section, "mm_rules": len(result[section])})
        return result
