"""
AI Provider Interfaces - Phase 24 ADR-052
==========================================
定義 AI Provider 標準介面，類比 MCP interfaces.py (ADR-015)

設計原則:
1. Interface 先行 (Contract-First) — leWOOOgo 積木化鐵律
2. Provider 只負責「呼叫 LLM」，不管閘門/Cache/Trace
3. AIResult 標準化回傳，供 AIRouter 統一處理

2026-04-02 ogt: Phase 24-A 建立
"""

from __future__ import annotations

import os
from dataclasses import dataclass, field
from typing import Any, Callable, Protocol, runtime_checkable

from src.plugins.mcp.interfaces import MCPTool, MCPToolResult


# =============================================================================
# AIResult — 標準化 AI 分析結果
# =============================================================================


@dataclass
class AIResult:
    """
    AI 分析標準化結果

    所有 Provider 必須回傳此格式，供 AIRouter 統一處理。
    類比 MCP 的 MCPToolResult。
    """

    raw_response: str
    success: bool
    provider: str  # e.g., "ollama", "gemini", "openclaw_nemo"
    from_cache: bool = False
    tokens: int = 0
    cost_usd: float = 0.0
    latency_ms: float = 0.0
    error: str | None = None

    def to_dict(self) -> dict:
        return {
            "raw_response": self.raw_response[:500],
            "success": self.success,
            "provider": self.provider,
            "from_cache": self.from_cache,
            "tokens": self.tokens,
            "cost_usd": self.cost_usd,
            "latency_ms": round(self.latency_ms, 2),
            "error": self.error,
        }


@dataclass
class ToolCallResult:
    """One tool_use result returned to an Agent Loop capable model."""

    tool_use_id: str
    tool_name: str
    output: Any
    success: bool = True
    error: str | None = None

    def to_dict(self) -> dict:
        return {
            "tool_use_id": self.tool_use_id,
            "tool_name": self.tool_name,
            "output": self.output,
            "success": self.success,
            "error": self.error,
        }


# =============================================================================
# AIProvider Protocol — 所有 LLM 引擎必須實作
# =============================================================================


@runtime_checkable
class AIProvider(Protocol):
    """
    AI Provider 標準介面 — Brain Layer 的 MCPToolProvider

    設計原則 (ADR-052 D3):
    - Provider 是「純粹的運算單元 (Stateless Compute Units)」
    - 所有閘門 (CB/RL/Sem)、Cache、Trace 由 AIRouter 管理
    - Provider 只負責: 接收 prompt → 呼叫 LLM → 回傳 AIResult

    隱私規則 (ADR-052 D7):
    - privacy_level="local" → 可處理 DIAGNOSE/CODE_REVIEW (機密資料)
    - privacy_level="cloud" → 禁止接收機密資料 (零信任)
    """

    @property
    def name(self) -> str:
        """Provider 唯一名稱 (e.g., 'ollama', 'gemini', 'openclaw_nemo')"""
        ...

    @property
    def is_enabled(self) -> bool:
        """根據環境變數 ENABLE_{NAME} 動態判斷是否啟用"""
        ...

    @property
    def capabilities(self) -> set[str]:
        """
        支援的能力集合

        標準能力:
        - "rca": Root Cause Analysis (Incident 仲裁)
        - "tool_calling": K8s Tool Calling (kubectl 指令生成)
        - "code_review": 代碼審查
        - "chat": 一般對話
        """
        ...

    @property
    def privacy_level(self) -> str:
        """
        隱私等級: "local" | "cloud"

        ADR-052 D7 + 首席架構師 Q2 裁示:
        - "local": 本地運算，可處理機密資料
        - "cloud": 雲端 API，禁止機密資料 (即使有企業協議)
        """
        ...

    async def analyze(
        self,
        prompt: str,
        context: dict[str, Any] | None = None,
    ) -> AIResult:
        """
        執行 AI 分析

        Args:
            prompt: 完整的 LLM prompt
            context: 額外上下文 (alert_type, target_resource 等)

        Returns:
            AIResult: 標準化結果
        """
        ...

    async def analyze_with_tools(
        self,
        prompt: str,
        available_tools: list[MCPTool],
        tool_executor: Callable[[str, dict[str, Any]], Any],
        max_iterations: int = 5,
        agent_role: str = "openclaw",
        context: dict[str, Any] | None = None,
    ) -> AIResult:
        """
        Agent Loop mode: let the model request MCP tools, receive tool results,
        and decide when it has enough evidence.

        Providers may implement native tool_use. Providers without native support
        should fail closed or fall back to analyze(); routing code decides whether
        fallback is acceptable for the current incident.
        """
        ...

    async def health_check(self) -> bool:
        """健康檢查 (供 /health endpoint 和 AIRouter 動態路由)"""
        ...

    async def close(self) -> None:
        """關閉 HTTP 連線 / 釋放資源 (shutdown hook, ADR-052 I5)"""
        ...


# =============================================================================
# 工具函數
# =============================================================================


def is_provider_enabled_by_env(provider_name: str) -> bool:
    """
    根據環境變數判斷 Provider 是否啟用

    環境變數格式: ENABLE_{PROVIDER_NAME_UPPER}
    例如: ENABLE_OLLAMA, ENABLE_GEMINI, ENABLE_OPENCLAW_NEMO

    Args:
        provider_name: Provider 名稱 (e.g., "ollama", "openclaw_nemo")

    Returns:
        bool: 是否啟用 (預設 True)
    """
    env_key = f"ENABLE_{provider_name.upper()}"
    return os.getenv(env_key, "true").lower() == "true"
