# src/anypoint_sdk/resources/policies.py
from __future__ import annotations

from dataclasses import asdict, dataclass
from typing import Any, Dict, Iterable, List, Optional

from .._http import HttpClient
from .._logging import LoggerLike, default_logger


@dataclass(frozen=True)
class ApiPolicy:
    policyId: int
    policyTemplateId: Optional[str] = None
    type: Optional[str] = None  # "system" or "custom"
    order: Optional[int] = None
    implementationAsset: Optional[Dict[str, Any]] = None
    configuration: Optional[Dict[str, Any]] = None


def _to_api_policy(obj: Dict[str, Any]) -> ApiPolicy:
    return ApiPolicy(
        policyId=int(obj.get("policyId", 0)),
        policyTemplateId=obj.get("policyTemplateId"),
        type=obj.get("type"),
        order=obj.get("order"),
        implementationAsset=obj.get("implementationAsset"),
        configuration=obj.get("configuration"),
    )


def _dedupe(policies: Iterable[ApiPolicy]) -> List[ApiPolicy]:
    seen: set[int] = set()
    out: List[ApiPolicy] = []
    for p in policies:
        if p.policyId and p.policyId not in seen:
            seen.add(p.policyId)
            out.append(p)
    return out


@dataclass(frozen=True)
class AutomatedPolicy:
    id: int
    assetId: Optional[str] = None
    assetVersion: Optional[str] = None
    order: Optional[int] = None
    disabled: Optional[bool] = None
    configurationData: Optional[Dict[str, Any]] = None
    implementationAssets: Optional[List[Dict[str, Any]]] = None  # list of dicts


def _to_automated_policy(obj: Dict[str, Any]) -> AutomatedPolicy:
    impl_assets = obj.get("implementationAssets")
    if not isinstance(impl_assets, list):
        impl_assets = []
    cfg = obj.get("configurationData")
    if not isinstance(cfg, dict):
        cfg = None
    return AutomatedPolicy(
        id=int(obj.get("id", 0)),
        assetId=obj.get("assetId"),
        assetVersion=obj.get("assetVersion"),
        order=obj.get("order"),
        disabled=obj.get("disabled"),
        configurationData=cfg,
        implementationAssets=[ia for ia in impl_assets if isinstance(ia, dict)],
    )


def _dedupe_automated(items: Iterable[AutomatedPolicy]) -> List[AutomatedPolicy]:
    seen: set[int] = set()
    out: List[AutomatedPolicy] = []
    for i in items:
        if i.id and i.id not in seen:
            seen.add(i.id)
            out.append(i)
    return out


class Policies:
    """
    API Manager policy resources, minimal surface for API-scoped policies.
    """

    def __init__(
        self, http: HttpClient, *, logger: Optional[LoggerLike] = None
    ) -> None:
        self._http = http
        self._log = logger or default_logger().child("resources.policies")

    def list_api_policies(
        self, org_id: str, env_id: str, api_id: int | str
    ) -> List[Dict[str, Any]]:
        """
        List policies applied to a specific API instance.

        Returns a list of dicts with keys:
        policyId, policyTemplateId, type, order, implementationAsset, configuration.
        """
        r = self._http.get(
            f"/apimanager/api/v1/organizations/{org_id}/environments/{env_id}/apis/{api_id}/policies"
        )
        payload = r.json() or {}
        raw = payload.get("policies")
        if not isinstance(raw, list):
            raw = []

        items: List[ApiPolicy] = []
        for obj in raw:
            if isinstance(obj, dict):
                items.append(_to_api_policy(obj))

        flat = [asdict(x) for x in _dedupe(items)]
        self._log.debug(
            "Listed %d API policies for org=%s env=%s api=%s",
            len(flat),
            org_id,
            env_id,
            api_id,
        )
        return flat

    def list_environment_automated_policies(
        self, org_id: str, env_id: str
    ) -> List[Dict[str, Any]]:
        """
        List automated policies applied at the environment scope.

        Returns a list of dicts with keys:
        id, assetId, assetVersion, order, disabled, configurationData, implementationAssets.
        Filters out items whose ruleOfApplication.environmentId is present and does not match env_id.
        """
        r = self._http.get(
            f"/apimanager/api/v1/organizations/{org_id}/automated-policies",
            params={"environmentId": env_id},
        )
        payload = r.json() or {}
        raw = payload.get("automatedPolicies")
        if not isinstance(raw, list):
            raw = []

        items: List[AutomatedPolicy] = []
        for obj in raw:
            if not isinstance(obj, dict):
                continue
            roa = obj.get("ruleOfApplication") or {}
            roa_env = None
            if isinstance(roa, dict):
                roa_env = roa.get("environmentId")
            if roa_env and str(roa_env) != str(env_id):
                # Defensive filter if server returns other env policies
                continue
            items.append(_to_automated_policy(obj))

        flat = [asdict(x) for x in _dedupe_automated(items)]
        self._log.debug(
            "Listed %d automated policies for org=%s env=%s", len(flat), org_id, env_id
        )
        return flat
