"""
Claude Provider - Phase 24 ADR-052
====================================
Anthropic Claude API (Claude Haiku 4.5, Tool Use 強制 JSON)

搬移自: openclaw.py _call_claude (L474-550)
特性: 最強推理、昂貴、CRITICAL/DELETE 強制使用

2026-04-02 ogt: Phase 24-A 從 openclaw.py 抽出
"""

from __future__ import annotations

import json
import time

import httpx
import structlog

from src.core.config import get_settings
from src.plugins.mcp.interfaces import MCPTool
from src.services.ai_providers.interfaces import AIResult, is_provider_enabled_by_env
from src.services.ai_providers.tool_schema import anthropic_tools_for_agent
from src.services.model_registry import get_model_registry

logger = structlog.get_logger(__name__)
settings = get_settings()


class ClaudeProvider:
    """
    Anthropic Claude Cloud Provider

    privacy_level: cloud (禁止機密資料)
    capabilities: rca, chat, code_review
    """

    def __init__(self) -> None:
        self._http_client: httpx.AsyncClient | None = None

    async def _get_client(self) -> httpx.AsyncClient:
        if self._http_client is None or self._http_client.is_closed:
            self._http_client = httpx.AsyncClient(timeout=httpx.Timeout(30.0, connect=10.0))
        return self._http_client

    @property
    def name(self) -> str:
        return "claude"

    @property
    def is_enabled(self) -> bool:
        if not settings.CLAUDE_API_KEY:
            return False
        return is_provider_enabled_by_env("claude")

    @property
    def capabilities(self) -> set[str]:
        return {"rca", "chat", "code_review"}

    @property
    def privacy_level(self) -> str:
        return "cloud"

    async def analyze(
        self,
        prompt: str,
        context: dict | None = None,
    ) -> AIResult:
        if not settings.CLAUDE_API_KEY:
            return AIResult(raw_response="", success=False, provider=self.name, error="CLAUDE_API_KEY not configured")

        start = time.perf_counter()
        try:
            client = await self._get_client()

            response = await client.post(
                "https://api.anthropic.com/v1/messages",
                headers={
                    "x-api-key": settings.CLAUDE_API_KEY,
                    "anthropic-version": "2023-06-01",
                    "content-type": "application/json",
                },
                json={
                    "model": get_model_registry().get_model("claude", "rca"),
                    "max_tokens": 2048,
                    "messages": [{"role": "user", "content": prompt}],
                    "tools": [{
                        "name": "submit_analysis",
                        "description": "Submit the RCA analysis result in structured format",
                        "input_schema": {
                            "type": "object",
                            "properties": {
                                "action_title": {"type": "string"},
                                "description": {"type": "string"},
                                "suggested_action": {"type": "string", "enum": ["RESTART_DEPLOYMENT", "DELETE_POD", "SCALE_DEPLOYMENT", "NO_ACTION"]},
                                "kubectl_command": {"type": "string"},
                                "target_resource": {"type": "string"},
                                "namespace": {"type": "string"},
                                "risk_level": {"type": "string", "enum": ["low", "medium", "critical"]},
                                "blast_radius": {
                                    "type": "object",
                                    "properties": {
                                        "affected_pods": {"type": "integer"},
                                        "estimated_downtime": {"type": "string"},
                                        "related_services": {"type": "array", "items": {"type": "string"}},
                                        "data_impact": {"type": "string", "enum": ["NONE", "READ_ONLY", "WRITE", "DESTRUCTIVE"]},
                                    },
                                    "required": ["affected_pods", "estimated_downtime", "related_services", "data_impact"],
                                },
                                "reasoning": {"type": "string"},
                                "deviation_analysis": {"type": "string"},
                                "confidence": {"type": "number"},
                                "affected_services": {"type": "array", "items": {"type": "string"}},
                            },
                            "required": ["action_title", "description", "suggested_action", "kubectl_command", "target_resource", "namespace", "risk_level", "blast_radius", "reasoning", "confidence"],
                        },
                    }],
                    "tool_choice": {"type": "tool", "name": "submit_analysis"},
                },
                timeout=30.0,
            )
            response.raise_for_status()
            data = response.json()

            # I2 修復: 追蹤 tokens/cost
            usage = data.get("usage", {})
            input_tokens = usage.get("input_tokens", 0)
            output_tokens = usage.get("output_tokens", 0)
            total_tokens = input_tokens + output_tokens
            # Claude Haiku 4.5: Input $1/1M, Output $5/1M
            cost_usd = (input_tokens * 0.000001) + (output_tokens * 0.000005)

            # 從 Tool Use 回應中提取 JSON
            for block in data.get("content", []):
                if block.get("type") == "tool_use" and block.get("name") == "submit_analysis":
                    tool_input = block.get("input", {})
                    latency = (time.perf_counter() - start) * 1000
                    logger.info("claude_provider_success", keys=list(tool_input.keys()), tokens=total_tokens, latency_ms=round(latency, 1))
                    return AIResult(
                        raw_response=json.dumps(tool_input),
                        success=True,
                        provider=self.name,
                        tokens=total_tokens,
                        cost_usd=cost_usd,
                        latency_ms=latency,
                    )

            # Fallback: text content
            for block in data.get("content", []):
                if block.get("type") == "text":
                    latency = (time.perf_counter() - start) * 1000
                    return AIResult(
                        raw_response=block.get("text", ""),
                        success=True,
                        provider=self.name,
                        tokens=total_tokens,
                        cost_usd=cost_usd,
                        latency_ms=latency,
                    )

            latency = (time.perf_counter() - start) * 1000
            return AIResult(raw_response="", success=False, provider=self.name, latency_ms=latency, error="No valid response")

        except Exception as e:
            latency = (time.perf_counter() - start) * 1000
            logger.warning("claude_provider_failed", error=str(e), latency_ms=round(latency, 1))
            return AIResult(raw_response="", success=False, provider=self.name, latency_ms=latency, error=str(e))

    async def analyze_with_tools(
        self,
        prompt: str,
        available_tools: list[MCPTool],
        tool_executor,
        max_iterations: int = 5,
        agent_role: str = "openclaw",
        context: dict | None = None,
    ) -> AIResult:
        """Run Claude in Agent Loop mode with MCP tool_use."""

        if not available_tools:
            return await self.analyze(prompt, context=context)
        if not settings.CLAUDE_API_KEY:
            return AIResult(raw_response="", success=False, provider=self.name, error="CLAUDE_API_KEY not configured")

        tools_schema = anthropic_tools_for_agent(available_tools, agent_role)
        if not tools_schema:
            return AIResult(
                raw_response="",
                success=False,
                provider=self.name,
                error=f"No MCP tools allowed for agent_role={agent_role}",
            )

        start = time.perf_counter()
        total_tokens = 0
        total_cost = 0.0
        messages: list[dict] = [{"role": "user", "content": prompt}]

        try:
            client = await self._get_client()
            last_text = ""

            for iteration in range(max_iterations):
                response = await client.post(
                    "https://api.anthropic.com/v1/messages",
                    headers={
                        "x-api-key": settings.CLAUDE_API_KEY,
                        "anthropic-version": "2023-06-01",
                        "content-type": "application/json",
                    },
                    json={
                        "model": get_model_registry().get_model("claude", "rca"),
                        "max_tokens": 2048,
                        "messages": messages,
                        "tools": tools_schema,
                    },
                    timeout=30.0,
                )
                response.raise_for_status()
                data = response.json()

                usage = data.get("usage", {})
                input_tokens = usage.get("input_tokens", 0)
                output_tokens = usage.get("output_tokens", 0)
                total_tokens += input_tokens + output_tokens
                total_cost += (input_tokens * 0.000001) + (output_tokens * 0.000005)

                content_blocks = data.get("content", [])
                text_blocks = [
                    block.get("text", "")
                    for block in content_blocks
                    if block.get("type") == "text"
                ]
                if text_blocks:
                    last_text = "\n".join(text_blocks)

                tool_uses = [
                    block for block in content_blocks
                    if block.get("type") == "tool_use"
                ]
                if not tool_uses:
                    latency = (time.perf_counter() - start) * 1000
                    logger.info(
                        "claude_agent_loop_success",
                        agent_role=agent_role,
                        iterations=iteration + 1,
                        tokens=total_tokens,
                        latency_ms=round(latency, 1),
                    )
                    return AIResult(
                        raw_response=last_text or json.dumps(data, ensure_ascii=False),
                        success=True,
                        provider=f"{self.name}_agent_loop",
                        tokens=total_tokens,
                        cost_usd=total_cost,
                        latency_ms=latency,
                    )

                tool_results = []
                for block in tool_uses:
                    tool_name = str(block.get("name", ""))
                    tool_input = block.get("input") or {}
                    result = await tool_executor(tool_name, tool_input)
                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": block.get("id"),
                        "content": json.dumps(
                            result.to_dict() if hasattr(result, "to_dict") else result,
                            ensure_ascii=False,
                            default=str,
                        ),
                    })

                messages.append({"role": "assistant", "content": content_blocks})
                messages.append({"role": "user", "content": tool_results})

            latency = (time.perf_counter() - start) * 1000
            return AIResult(
                raw_response=last_text,
                success=False,
                provider=f"{self.name}_agent_loop",
                tokens=total_tokens,
                cost_usd=total_cost,
                latency_ms=latency,
                error=f"Agent loop exceeded max_iterations={max_iterations}",
            )

        except Exception as e:
            latency = (time.perf_counter() - start) * 1000
            logger.warning(
                "claude_agent_loop_failed",
                agent_role=agent_role,
                error=str(e),
                latency_ms=round(latency, 1),
            )
            return AIResult(
                raw_response="",
                success=False,
                provider=f"{self.name}_agent_loop",
                tokens=total_tokens,
                cost_usd=total_cost,
                latency_ms=latency,
                error=str(e),
            )

    async def health_check(self) -> bool:
        return bool(settings.CLAUDE_API_KEY)

    async def close(self) -> None:
        if self._http_client:
            await self._http_client.aclose()
            self._http_client = None
