"""Claude Agent SDK adapter. Section 16 of the design.

Gives finer-grained event and tool control than the print-mode CLI while satisfying the
same AgentAdapter contract, so the state machine does not change. The SDK is an optional
dependency: absent it, the adapter reports itself incompatible instead of failing at run
time, and the CLI adapter stays the default.
"""

from __future__ import annotations

import asyncio
from pathlib import Path

from .base import AgentRequest, AgentResult
from .capability import Capability
from .fake import SCHEMAS
from .parse import find_session_id, read_usage
from .prompt import stage_prompt


def _sdk():
    try:
        import claude_agent_sdk  # noqa: PLC0415 - optional dependency probed at call time

        return claude_agent_sdk
    except ImportError:
        return None


class ClaudeSDKAdapter:
    def __init__(self, log_root: str | Path = ".dual-agent/logs", timeout_s: float = 900, **_: object) -> None:
        self.log_root = Path(log_root)
        self.timeout_s = timeout_s

    def probe(self) -> Capability:
        module = _sdk()
        version = getattr(module, "__version__", "") if module else ""
        present = module is not None
        return Capability(
            executable="claude-agent-sdk",
            version=version,
            help_hash="",
            features={"sdk": present, "resume": present},
            compatible=present,
            missing_features=[] if present else ["sdk"],
            optional_features={"resume": present},
        )

    def run(self, request: AgentRequest, on_start=None) -> AgentResult:
        module = _sdk()
        if module is None:
            return AgentResult(
                "AGENT_FAILURE",
                error="claude-agent-sdk is not installed; pip install dual-agent-orchestrator[sdk]",
            )
        schema_type = SCHEMAS.get(request.stage)
        if schema_type is None:
            return AgentResult("PROTOCOL", error=f"unsupported stage {request.stage}")
        try:
            payload = asyncio.run(self._query(module, request, schema_type))
        except Exception as error:  # noqa: BLE001 - SDK failures are untrusted input
            return AgentResult("AGENT_FAILURE", error=str(error))
        return _to_result(payload, schema_type)

    async def _query(self, module, request: AgentRequest, schema_type) -> dict:
        options = module.ClaudeAgentOptions(
            cwd=str(request.worktree_path),
            permission_mode="acceptEdits" if request.stage in {"IMPLEMENT", "FIX"} else "plan",
            resume=request.session_id,
        )
        collected: dict = {"messages": []}
        async for message in module.query(prompt=stage_prompt(request), options=options):
            collected["messages"].append(_as_dict(message))
        return collected


def _as_dict(message: object) -> dict:
    for attribute in ("model_dump", "to_dict", "__dict__"):
        value = getattr(message, attribute, None)
        if callable(value):
            return dict(value())
        if isinstance(value, dict):
            return dict(value)
    return {"repr": repr(message)}


def _to_result(payload: dict, schema_type) -> AgentResult:
    """The last message carrying structured output wins; free text never advances state."""
    session_id, usage, structured = None, None, None
    for message in payload.get("messages", []):
        session_id = session_id or find_session_id(message)
        usage = read_usage(message) or usage
        candidate = message.get("structured_output") or message.get("result")
        if candidate is not None:
            structured = candidate
    if structured is None:
        return AgentResult("PROTOCOL", error="SDK returned no structured output", session_id=session_id, usage=usage)
    try:
        artifact = (
            schema_type.model_validate_json(structured)
            if isinstance(structured, str)
            else schema_type.model_validate(structured)
        )
    except Exception as error:  # noqa: BLE001
        return AgentResult("PROTOCOL", error=str(error), session_id=session_id, usage=usage)
    return AgentResult("SUCCESS", artifact=artifact, session_id=session_id, usage=usage)
