from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Dict

from mockmesh.core.logger import get_logger

_log = get_logger("response.engine")


@dataclass
class MockResponse:
    status:  int = 200
    headers: Dict[str, str] = field(default_factory=dict)
    body:    Any = None
    tier:    str = "unknown"

    def resolve_body(self, **context: Any) -> Any:
        return self.body


class ResponseEngine:
    def __init__(self) -> None:
        self._resolver = None
        self._rules: dict = {}
        self._log = get_logger("response")

    def load(self, config) -> None:
        from mockmesh.config.resolver import ConfigResolver
        if isinstance(config, ConfigResolver):
            self._resolver = config
        else:
            self._rules.clear()
            for section, data in config.items():
                if isinstance(data, dict) and "rules" in data:
                    self._rules[section] = data["rules"]

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

    def resolve_http(self, url: str, method: str, **ctx: Any) -> MockResponse:
        if self._resolver:
            res = self._resolver.resolve_http(url, method, **ctx)
            self._log.info("HTTP resolved",
                           extra={"mm_url": url, "mm_method": method,
                                  "mm_tier": res.tier, "mm_status": res.status})
            return MockResponse(status=res.status, headers=res.headers,
                                body=res.body, tier=res.tier.value)
        import fnmatch
        for section in ("http", "azure", "gcp"):
            for rule in self._rules.get(section, []):
                m  = rule.get("match", {})
                rm = m.get("method", "*")
                if rm != "*" and rm.upper() != method.upper():
                    continue
                if "url" in m and m["url"] != "*" and not fnmatch.fnmatch(url, m["url"]):
                    continue
                if "url_contains" in m and m["url_contains"] not in url:
                    continue
                resp = rule.get("response", {})
                return MockResponse(status=resp.get("status", 200),
                                    headers=resp.get("headers", {}),
                                    body=resp.get("body"))
        return MockResponse(status=200, body={"mocked": True})

    def resolve_aws(self, service: str, operation: str, **ctx: Any) -> MockResponse:
        if self._resolver:
            res = self._resolver.resolve_aws(service, operation, **ctx)
            self._log.info("AWS resolved",
                           extra={"mm_service": service, "mm_operation": operation,
                                  "mm_tier": res.tier, "mm_status": res.status})
            return MockResponse(status=res.status, headers=res.headers,
                                body=res.body, tier=res.tier.value)
        for rule in self._rules.get("aws", []):
            m = rule.get("match", {})
            if (m.get("service", "*") in ("*", service)
                    and m.get("operation", "*") in ("*", operation)):
                resp = rule.get("response", {})
                return MockResponse(status=resp.get("status", 200), body=resp.get("body", {}))
        return MockResponse(status=200, body={"mocked": True})

    def resolve_gcp(self, service: str, operation: str, **ctx: Any) -> MockResponse:
        if self._resolver:
            res = self._resolver.resolve_gcp(service, operation, **ctx)
            self._log.info("GCP resolved",
                           extra={"mm_service": service, "mm_operation": operation,
                                  "mm_tier": res.tier, "mm_status": res.status})
            return MockResponse(status=res.status, headers=res.headers,
                                body=res.body, tier=res.tier.value)
        for rule in self._rules.get("gcp", []):
            m = rule.get("match", {})
            if (m.get("service", "*") in ("*", service)
                    and m.get("operation", "*") in ("*", operation)):
                resp = rule.get("response", {})
                return MockResponse(status=resp.get("status", 200), body=resp.get("body", {}))
        return MockResponse(status=200, body={"mocked": True})

    def resolve_kafka(self, topic: str, **ctx: Any) -> dict:
        if self._resolver:
            _, raw = self._resolver.resolve_kafka(topic, **ctx)
            return raw
        for rule in self._rules.get("kafka", []):
            t = rule.get("match", {}).get("topic", "*")
            if t in ("*", topic):
                return rule
        return {}

    def resolve_rabbitmq(self, queue: str = "", exchange: str = "", **ctx: Any) -> dict:
        if self._resolver:
            _, raw = self._resolver.resolve_rabbitmq(queue, exchange, **ctx)
            return raw
        for rule in self._rules.get("rabbitmq", []):
            m = rule.get("match", {})
            if (m.get("queue", "*") in ("*", queue)
                    and m.get("exchange", "*") in ("*", exchange)):
                return rule
        return {}
