from __future__ import annotations

import fnmatch
from dataclasses import dataclass
from enum import Enum
from typing import Any, Dict, List

from mockmesh.core.logger import get_logger

_log = get_logger("config.resolver")


class ResolutionTier(str, Enum):
    USER    = "user"
    DEFAULT = "default"
    GENERIC = "generic"


@dataclass
class Resolution:
    tier:    ResolutionTier
    rule:    dict
    section: str = ""

    @property
    def response(self) -> dict:
        return self.rule.get("response", {})

    @property
    def status(self) -> int:
        return self.response.get("status", 200)

    @property
    def headers(self) -> dict:
        return self.response.get("headers", {})

    @property
    def body(self) -> Any:
        return self.response.get("body")


class ConfigResolver:

    def __init__(self, rules: Dict[str, List[dict]]) -> None:
        self._rules = rules

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

    def resolve_http(self, url: str, method: str, **ctx: Any) -> Resolution:
        for section in ("http", "azure", "gcp"):
            for rule in self._rules.get(section, []):
                if _match_http(rule.get("match", {}), url, method):
                    tier = _rule_tier(rule)
                    _log.debug("HTTP rule matched",
                               extra={"mm_tier": tier, "mm_url": url, "mm_method": method,
                                      "mm_section": section, "mm_desc": rule.get("description", "")})
                    return Resolution(tier=tier, section=section, rule=rule)

        _log.warning("HTTP: no rule – generic fallback",
                     extra={"mm_url": url, "mm_method": method})
        return Resolution(tier=ResolutionTier.GENERIC, section="http",
                          rule={"response": {"status": 200, "headers": {},
                                             "body": {"mocked": True, "source": "generic-fallback"}}})

    def resolve_aws(self, service: str, operation: str, **ctx: Any) -> Resolution:
        return self._resolve_by_match(
            "aws", {"service": service, "operation": operation},
        )

    def resolve_gcp(self, service: str, operation: str, **ctx: Any) -> Resolution:
        return self._resolve_by_match(
            "gcp", {"service": service, "operation": operation},
        )

    def resolve_kafka(self, topic: str, **ctx: Any) -> tuple[Resolution, dict]:
        return self._resolve_messaging("kafka", {"topic": topic})

    def resolve_rabbitmq(self, queue: str = "", exchange: str = "",
                         **ctx: Any) -> tuple[Resolution, dict]:
        return self._resolve_messaging("rabbitmq", {"queue": queue, "exchange": exchange})

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

    def _resolve_by_match(self, section: str, matchers: dict) -> Resolution:
        for rule in self._rules.get(section, []):
            m = rule.get("match", {})
            if all(m.get(k, "*") in ("*", v) for k, v in matchers.items()):
                tier = _rule_tier(rule)
                _log.debug(f"{section.upper()} rule matched",
                           extra={"mm_tier": tier, **{f"mm_{k}": v for k, v in matchers.items()}})
                return Resolution(tier=tier, section=section, rule=rule)
        _log.warning(f"{section.upper()}: no rule – generic fallback",
                     extra={f"mm_{k}": v for k, v in matchers.items()})
        return Resolution(tier=ResolutionTier.GENERIC, section=section,
                          rule={"response": {"status": 200,
                                             "body": {"mocked": True, "source": "generic-fallback"}}})

    def _resolve_messaging(self, section: str, matchers: dict) -> tuple[Resolution, dict]:
        for rule in self._rules.get(section, []):
            m = rule.get("match", {})
            if all(m.get(k, "*") in ("*", v) for k, v in matchers.items()):
                tier = _rule_tier(rule)
                _log.debug(f"{section.upper()} rule matched",
                           extra={"mm_tier": tier, **{f"mm_{k}": v for k, v in matchers.items()}})
                return Resolution(tier=tier, section=section, rule=rule), rule
        return Resolution(tier=ResolutionTier.GENERIC, section=section, rule={}), {}

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

def _rule_tier(rule: dict) -> ResolutionTier:
    raw = rule.get("_tier", "default")
    return ResolutionTier.USER if raw == "user" else ResolutionTier.DEFAULT


def _match_http(match: dict, url: str, method: str) -> bool:
    rule_method = match.get("method", "*")
    if rule_method != "*" and rule_method.upper() != method.upper():
        return False
    if "url" in match:
        pattern = match["url"]
        if pattern != "*" and not fnmatch.fnmatch(url, pattern):
            return False
    if "url_contains" in match:
        if match["url_contains"] not in url:
            return False
    return True
