import asyncio
import json
from typing import Any

from claude_agent_sdk import (
    ClaudeAgentOptions,
    ProcessError,
    ResultMessage,
    query,
)

from home_agent.contracts.types import Item, LLMResult, LLMUsage, RetrievedMatch
from home_agent.core.logging import get_logger
from home_agent.llm.runner import (
    CLASSIFICATION_SCHEMA,
    _decision_from_payload,
    _render_retrieval_prompt_suffix,
)


class ClaudeSDKRunner:
    """LLMRunner implementation using claude-agent-sdk instead of CLI subprocess."""

    def __init__(
        self,
        model: str = "claude-sonnet-4-5",
        timeout_seconds: float = 120,
    ) -> None:
        self.model: str = model
        self.timeout_seconds: float = timeout_seconds

    def classify(
        self, item: Item, retrieved_matches: list[RetrievedMatch] | None = None
    ) -> LLMResult:
        logger = get_logger(__name__).bind(
            llm_provider="claude-sdk",
            provider=item.provider,
            external_id=item.external_id,
        )
        prompt = (
            "Return JSON with score(0-1), label(low|medium|high|critical), reasons(array). "
            f"Subject: {item.subject}; Sender: {item.sender}; "
            f"Metadata: {json.dumps(item.metadata)}"
        )
        prompt += _render_retrieval_prompt_suffix(retrieved_matches)
        logger.debug("llm.classify.start", retrieved_match_count=len(retrieved_matches or []))

        options = ClaudeAgentOptions(
            model=self.model,
            output_format={
                "type": "json_schema",
                "schema": CLASSIFICATION_SCHEMA,
            },
            max_turns=1,
            permission_mode="bypassPermissions",
        )

        try:
            result_msg = asyncio.run(self._query(prompt, options))
        except ProcessError as exc:
            logger.error("llm.classify.failed", exit_code=exc.exit_code)
            msg = f"claude-sdk call failed (exit {exc.exit_code})"
            raise RuntimeError(msg) from exc

        structured = result_msg.structured_output
        if not isinstance(structured, dict):
            msg = (
                "claude-agent-sdk did not return structured_output. "
                "Ensure output_format with json_schema is supported."
            )
            raise RuntimeError(msg)
        parsed: dict[str, object] = {str(k): v for k, v in structured.items()}

        usage_raw: dict[str, Any] = result_msg.usage or {}
        usage = LLMUsage(
            provider="claude-sdk",
            model=self.model,
            total_cost_usd=result_msg.total_cost_usd,
            input_tokens=_as_int(usage_raw.get("input_tokens")),
            output_tokens=_as_int(usage_raw.get("output_tokens")),
            raw=usage_raw,
        )

        decision = _decision_from_payload(parsed, used_llm=True)
        logger.debug(
            "llm.classify.completed",
            label=decision.label.value,
            score=decision.score,
            total_cost_usd=usage.total_cost_usd,
        )
        return LLMResult(decision=decision, usage=usage, raw_response=usage_raw)

    @staticmethod
    async def _query(prompt: str, options: ClaudeAgentOptions) -> ResultMessage:
        result: ResultMessage | None = None
        async for message in query(prompt=prompt, options=options):
            if isinstance(message, ResultMessage):
                result = message
        if result is None:
            msg = "claude-agent-sdk returned no ResultMessage"
            raise RuntimeError(msg)
        return result


def _as_int(value: object) -> int | None:
    if value is None:
        return None
    if isinstance(value, int):
        return value
    if isinstance(value, float):
        return int(value)
    if isinstance(value, str):
        try:
            return int(value)
        except ValueError:
            return None
    return None
