"""
Resource Resolver - ADR-016 K8s 資源動態驗證
=============================================

在 AI 產生 kubectl 指令後，動態驗證資源是否存在於 K8s 叢集中。
若不存在，嘗試模糊匹配或回報需人工確認。

流程：
1. 正規化資源名稱 (k8s_naming.py)
2. 調用 MCP Tool 驗證資源存在性
3. 模糊匹配 namespace 內的 Deployments
4. 回傳匹配結果或候選列表

版本: v1.1
建立: 2026-03-26 (台北時區)
建立者: Claude Code (首席架構師)
修改: 2026-03-26 (台北) - 新增 IResourceResolver Protocol (P2 架構改進)

@see docs/adr/ADR-016-k8s-resource-naming.md
"""

from dataclasses import dataclass, field
from difflib import SequenceMatcher
from typing import Protocol, runtime_checkable

import structlog

from src.utils.k8s_naming import (
    ResourceType,
    extract_resource_hints,
    normalize_resource_name,
)

logger = structlog.get_logger(__name__)


# =============================================================================
# Types
# =============================================================================

@dataclass
class ResolveResult:
    """資源解析結果"""
    success: bool
    resource_name: str | None
    namespace: str | None
    resource_type: ResourceType
    confidence: float  # 0.0 - 1.0
    is_k8s_resource: bool = True
    requires_confirmation: bool = False
    candidates: list[str] = field(default_factory=list)
    note: str | None = None
    original_input: str = ""


@dataclass
class K8sResource:
    """K8s 資源資訊"""
    name: str
    namespace: str
    kind: str  # Deployment, StatefulSet, Pod, etc.
    replicas: int | None = None
    ready: bool = True


# =============================================================================
# Protocol Interface (Phase 17 P2 - 架構審查)
# =============================================================================

@runtime_checkable
class IResourceResolver(Protocol):
    """
    ResourceResolver 介面定義

    用途:
    - 依賴注入 (DI) 時的型別約束
    - 測試時 Mock 的型別檢查
    - 符合 leWOOOgo 積木化規範

    @see feedback_resource_resolver_di.md
    """

    async def resolve(
        self,
        raw_resource: str,
        namespace: str = "awoooi-prod",
        resource_kind: str = "deployment",
    ) -> ResolveResult:
        """解析原始資源名稱為有效的 K8s 資源"""
        ...


# =============================================================================
# Resource Resolver Implementation
# =============================================================================

class ResourceResolver:
    """
    K8s 資源名稱解析器 - 確保 kubectl 指令有效

    整合：
    - 靜態正規化 (k8s_naming.py)
    - 動態驗證 (MCP K8s Tool)
    - 模糊匹配 (Levenshtein distance)
    """

    def __init__(self):
        self._cached_resources: dict[str, list[K8sResource]] = {}
        self._cache_ttl: int = 60  # 快取 60 秒

    async def resolve(
        self,
        raw_resource: str,
        namespace: str = "awoooi-prod",
        resource_kind: str = "deployment",
    ) -> ResolveResult:
        """
        解析原始資源名稱為有效的 K8s 資源

        Args:
            raw_resource: 原始資源名稱 (可能是 URL、域名、或 K8s 名稱)
            namespace: 目標命名空間
            resource_kind: 資源類型 (deployment, statefulset, pod)

        Returns:
            ResolveResult: 解析結果
        """
        logger.info(
            "resource_resolve_start",
            raw=raw_resource,
            namespace=namespace,
            kind=resource_kind,
        )

        # Step 1: 靜態正規化
        normalized = normalize_resource_name(raw_resource, namespace)

        # 非 K8s 資源直接返回
        if not normalized.is_k8s_resource:
            return ResolveResult(
                success=True,
                resource_name=normalized.normalized,
                namespace=None,
                resource_type=ResourceType.UNKNOWN,
                confidence=normalized.confidence,
                is_k8s_resource=False,
                note=normalized.note,
                original_input=raw_resource,
            )

        # 正規化失敗
        if not normalized.success or not normalized.normalized:
            return ResolveResult(
                success=False,
                resource_name=None,
                namespace=namespace,
                resource_type=ResourceType.UNKNOWN,
                confidence=0.0,
                requires_confirmation=True,
                note=normalized.note,
                original_input=raw_resource,
            )

        # Step 2: 動態驗證 (調用 K8s API)
        resource_exists = await self._check_resource_exists(
            normalized.normalized,
            normalized.namespace or namespace,
            resource_kind,
        )

        if resource_exists:
            logger.info(
                "resource_verified",
                resource=normalized.normalized,
                namespace=normalized.namespace or namespace,
            )
            return ResolveResult(
                success=True,
                resource_name=normalized.normalized,
                namespace=normalized.namespace or namespace,
                resource_type=normalized.resource_type,
                confidence=1.0,
                note="Verified via K8s API",
                original_input=raw_resource,
            )

        # Step 3: 模糊匹配
        candidates = await self._fuzzy_match(
            raw_resource,
            normalized.namespace or namespace,
            resource_kind,
        )

        if len(candidates) == 1:
            best_match = candidates[0]
            logger.info(
                "resource_fuzzy_matched",
                original=raw_resource,
                matched=best_match,
            )
            return ResolveResult(
                success=True,
                resource_name=best_match,
                namespace=normalized.namespace or namespace,
                resource_type=normalized.resource_type,
                confidence=0.0,  # 🔴 模糊匹配，非 AI
                note=f"Fuzzy matched from '{raw_resource}'",
                original_input=raw_resource,
            )

        if len(candidates) > 1:
            logger.warning(
                "resource_multiple_matches",
                original=raw_resource,
                candidates=candidates,
            )
            return ResolveResult(
                success=False,
                resource_name=None,
                namespace=normalized.namespace or namespace,
                resource_type=normalized.resource_type,
                confidence=0.0,
                requires_confirmation=True,
                candidates=candidates,
                note=f"Multiple matches for '{raw_resource}': {candidates}",
                original_input=raw_resource,
            )

        # Step 4: 無匹配
        logger.warning(
            "resource_not_found",
            original=raw_resource,
            normalized=normalized.normalized,
            namespace=normalized.namespace or namespace,
        )
        return ResolveResult(
            success=False,
            resource_name=normalized.normalized,
            namespace=normalized.namespace or namespace,
            resource_type=normalized.resource_type,
            confidence=0.0,
            requires_confirmation=True,
            note=f"Resource '{normalized.normalized}' not found in namespace '{normalized.namespace or namespace}'",
            original_input=raw_resource,
        )

    async def _check_resource_exists(
        self,
        name: str,
        namespace: str,
        kind: str = "deployment",
    ) -> bool:
        """
        透過 MCP K8s Tool 檢查資源是否存在

        Args:
            name: 資源名稱
            namespace: 命名空間
            kind: 資源類型

        Returns:
            bool: 是否存在
        """
        try:
            # 嘗試導入 MCP Registry
            from src.plugins.mcp.registry import get_mcp_registry

            registry = get_mcp_registry()
            result = await registry.call_tool(
                tool_name="kubectl_get",
                arguments={
                    "resource": f"{kind}s",  # deployments, statefulsets, pods
                    "name": name,
                    "namespace": namespace,
                },
            )

            if result.success and result.data:
                # 檢查是否真的找到資源
                data = result.data
                if isinstance(data, dict):
                    # 單一資源
                    return data.get("metadata", {}).get("name") == name
                elif isinstance(data, list):
                    # 資源列表
                    return any(
                        r.get("metadata", {}).get("name") == name
                        for r in data
                    )
            return False

        except ImportError:
            logger.warning(
                "mcp_registry_not_available",
                note="Falling back to static validation only",
            )
            return False
        except Exception as e:
            logger.warning(
                "k8s_check_failed",
                resource=name,
                namespace=namespace,
                error=str(e),
            )
            return False

    async def _fuzzy_match(
        self,
        raw_resource: str,
        namespace: str,
        kind: str = "deployment",
    ) -> list[str]:
        """
        在 namespace 內模糊匹配資源

        Args:
            raw_resource: 原始輸入
            namespace: 命名空間
            kind: 資源類型

        Returns:
            list[str]: 匹配的資源名稱列表 (按相似度排序)
        """
        try:
            # 取得 namespace 內所有資源
            resources = await self._list_resources(namespace, kind)

            if not resources:
                return []

            # 提取關鍵字
            hints = extract_resource_hints(raw_resource)

            # 計算相似度
            scored: list[tuple[str, float]] = []
            for res in resources:
                score = self._calculate_similarity(res.name, hints, raw_resource)
                if score > 0.3:  # 閾值
                    scored.append((res.name, score))

            # 排序並返回
            scored.sort(key=lambda x: x[1], reverse=True)
            return [name for name, _ in scored[:5]]  # 最多 5 個候選

        except Exception as e:
            logger.warning(
                "fuzzy_match_failed",
                error=str(e),
            )
            return []

    async def _list_resources(
        self,
        namespace: str,
        kind: str = "deployment",
    ) -> list[K8sResource]:
        """
        列出 namespace 內所有指定類型的資源
        """
        try:
            from src.plugins.mcp.registry import get_mcp_registry

            registry = get_mcp_registry()
            result = await registry.call_tool(
                tool_name="kubectl_get",
                arguments={
                    "resource": f"{kind}s",
                    "namespace": namespace,
                },
            )

            if result.success and result.data:
                resources: list[K8sResource] = []
                items = result.data if isinstance(result.data, list) else [result.data]

                for item in items:
                    if isinstance(item, dict):
                        metadata = item.get("metadata", {})
                        spec = item.get("spec", {})
                        resources.append(K8sResource(
                            name=metadata.get("name", ""),
                            namespace=metadata.get("namespace", namespace),
                            kind=kind,
                            replicas=spec.get("replicas"),
                        ))

                return resources

            return []

        except Exception as e:
            logger.warning(
                "list_resources_failed",
                namespace=namespace,
                kind=kind,
                error=str(e),
            )
            return []

    def _calculate_similarity(
        self,
        resource_name: str,
        hints: list[str],
        original: str,
    ) -> float:
        """
        計算資源名稱與輸入的相似度

        綜合考慮：
        1. 直接子字串匹配
        2. 關鍵字匹配
        3. Levenshtein 距離
        """
        score = 0.0
        name_lower = resource_name.lower()
        original_lower = original.lower()

        # 1. 直接包含關係
        if name_lower in original_lower or original_lower in name_lower:
            score += 0.5

        # 2. 關鍵字匹配
        matched_hints = sum(1 for h in hints if h in name_lower)
        if hints:
            score += (matched_hints / len(hints)) * 0.3

        # 3. 序列相似度
        ratio = SequenceMatcher(None, name_lower, original_lower).ratio()
        score += ratio * 0.2

        return min(score, 1.0)


# =============================================================================
# Dependency Injection Support (Phase 17 P2 改進)
# =============================================================================

_resolver: IResourceResolver | None = None


def get_resource_resolver() -> IResourceResolver:
    """
    取得 ResourceResolver 實例

    支援兩種模式：
    1. 預設: 使用內部 singleton (向後相容)
    2. DI: 透過 set_resource_resolver() 注入自訂實例

    測試時可使用 set_resource_resolver(mock_resolver) 注入 mock

    Returns:
        IResourceResolver: 實作 IResourceResolver Protocol 的實例
    """
    global _resolver
    if _resolver is None:
        _resolver = ResourceResolver()
    return _resolver


def set_resource_resolver(resolver: IResourceResolver | None) -> None:
    """
    注入 ResourceResolver 實例 (用於 DI 或測試)

    Args:
        resolver: 自訂 ResourceResolver 實例，None 則重置為預設

    Example:
        # 測試時注入 mock
        mock_resolver = MockResourceResolver()
        set_resource_resolver(mock_resolver)

        # 測試後重置
        set_resource_resolver(None)
    """
    global _resolver
    _resolver = resolver
