from __future__ import annotations

from pathlib import Path
from typing import Any

from cybervisor.adapters.base import BaseSubprocessAdapter
from cybervisor.adapters.claude.stream import parse_output_line as parse_claude_output_line
from cybervisor.adapters.types import AdapterCapabilities, AdapterDescriptor, LaunchRequest, PreflightRequirements
from cybervisor.stream_logging import CanonicalLogEvent

_CLAUDE_STOP_MATCHER = ""
_CLAUDE_STOP_TIMEOUT_SECONDS = 120


class ClaudeAdapter(BaseSubprocessAdapter):
    descriptor = AdapterDescriptor(
        name="claude",
        display_name="Claude Code",
        directory=Path(__file__).resolve().parent,
        capabilities=AdapterCapabilities(supports_hooks=True, supports_structured_streams=True),
    )

    def build_command(self, request: LaunchRequest) -> list[str]:
        return [
            "claude",
            "--dangerously-skip-permissions",
            "--print",
            request.prompt,
            "--verbose",
            "--output-format",
            "stream-json",
        ]

    def parse_output_line(self, line: str) -> list[CanonicalLogEvent]:
        return parse_claude_output_line(line)

    def should_suppress_event(self, event: CanonicalLogEvent, saw_hook_feedback: bool) -> bool:
        return saw_hook_feedback and event.kind == "completed" and event.status == "success"

    def preflight_requirements(self) -> PreflightRequirements:
        return PreflightRequirements(
            required_binaries=("claude", "uv"),
            required_environment_variables=("CYBERVISOR_LLM_API_KEY",),
        )

    def settings_path(self) -> Path | None:
        return Path(".claude/settings.json")

    def install_hook_settings(self, payload: dict[str, Any], runtime_config_path: Path) -> dict[str, Any]:
        hooks = payload.get("hooks")
        if not isinstance(hooks, dict):
            hooks = {}
            payload["hooks"] = hooks

        hooks.pop("on_output", None)
        stop_event = hooks.get("Stop")
        if not isinstance(stop_event, list):
            stop_event = []
            hooks["Stop"] = stop_event

        desired_hook = {
            "type": "command",
            "command": f"uv run cybervisor-agent-hook --config {runtime_config_path.resolve()}",
            "timeout": _CLAUDE_STOP_TIMEOUT_SECONDS,
        }

        for block in stop_event:
            if not isinstance(block, dict):
                continue
            if str(block.get("matcher", _CLAUDE_STOP_MATCHER)) != _CLAUDE_STOP_MATCHER:
                continue
            block_hooks = block.get("hooks")
            if isinstance(block_hooks, list) and any(isinstance(item, dict) and item == desired_hook for item in block_hooks):
                return payload

        stop_event.append({"matcher": _CLAUDE_STOP_MATCHER, "hooks": [desired_hook]})
        return payload

    def remove_hook_settings(self, payload: dict[str, Any], runtime_config_path: Path) -> dict[str, Any]:
        hooks = payload.get("hooks")
        if not isinstance(hooks, dict):
            return payload

        stop_event = hooks.get("Stop")
        if not isinstance(stop_event, list):
            return payload

        desired_hook = {
            "type": "command",
            "command": f"uv run cybervisor-agent-hook --config {runtime_config_path.resolve()}",
            "timeout": _CLAUDE_STOP_TIMEOUT_SECONDS,
        }
        filtered_stop_event: list[Any] = []
        for block in stop_event:
            if not isinstance(block, dict):
                filtered_stop_event.append(block)
                continue
            if str(block.get("matcher", _CLAUDE_STOP_MATCHER)) != _CLAUDE_STOP_MATCHER:
                filtered_stop_event.append(block)
                continue
            block_hooks = block.get("hooks")
            if not isinstance(block_hooks, list):
                filtered_stop_event.append(block)
                continue
            filtered_hooks = [item for item in block_hooks if not (isinstance(item, dict) and item == desired_hook)]
            if filtered_hooks:
                updated_block = dict(block)
                updated_block["hooks"] = filtered_hooks
                filtered_stop_event.append(updated_block)

        if filtered_stop_event:
            hooks["Stop"] = filtered_stop_event
        else:
            hooks.pop("Stop", None)

        if not hooks:
            payload.pop("hooks", None)
        return payload
