from __future__ import annotations

import os
from pathlib import Path
from typing import Any

from cybervisor.adapters.base import BaseSubprocessAdapter
from cybervisor.adapters.gemini.stream import GeminiStreamParser
from cybervisor.adapters.types import AdapterCapabilities, AdapterDescriptor, LaunchRequest, PreflightRequirements, RunningProcess
from cybervisor.stream_logging import CanonicalLogEvent

_GEMINI_HOOK_NAME = "cybervisor-agent-hook"
_GEMINI_HOOK_MATCHER = ".*"


class GeminiAdapter(BaseSubprocessAdapter):
    descriptor = AdapterDescriptor(
        name="gemini",
        display_name="Gemini CLI",
        directory=Path(__file__).resolve().parent,
        capabilities=AdapterCapabilities(supports_hooks=True, supports_structured_streams=True),
    )

    def __init__(self) -> None:
        self._stream_parser = GeminiStreamParser()

    def build_command(self, request: LaunchRequest) -> list[str]:
        cmd = ["gemini", "--yolo"]

        # Default to sandboxing, but disable if GEMINI_SANDBOX=false or in a container.
        should_sandbox = True
        if os.environ.get("GEMINI_SANDBOX", "").lower() == "false":
            should_sandbox = False
        elif self._is_container():
            should_sandbox = False

        if should_sandbox:
            cmd.append("--sandbox")

        cmd.extend(["--prompt", request.prompt, "--output-format", "stream-json"])
        return cmd

    def start(self, request: LaunchRequest) -> RunningProcess:
        self._stream_parser.reset()
        return super().start(request)

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

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

    def flush_pending_events(self) -> list[CanonicalLogEvent]:
        return self._stream_parser.flush_pending_events()

    def settings_path(self) -> Path | None:
        return Path(".gemini/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)

        after_agent = hooks.get("AfterAgent")
        if not isinstance(after_agent, list):
            after_agent = []
            hooks["AfterAgent"] = after_agent

        matcher_block: dict[str, Any] | None = None
        for block in after_agent:
            if not isinstance(block, dict):
                continue
            if block.get("matcher") != _GEMINI_HOOK_MATCHER:
                continue
            if isinstance(block.get("hooks"), list):
                matcher_block = block
                break

        if matcher_block is None:
            matcher_block = {"matcher": _GEMINI_HOOK_MATCHER, "hooks": []}
            after_agent.append(matcher_block)

        block_hooks = matcher_block.get("hooks")
        if not isinstance(block_hooks, list):
            block_hooks = []
            matcher_block["hooks"] = block_hooks

        command = f"uv run cybervisor-agent-hook --config {runtime_config_path.resolve()}"
        if not any(isinstance(item, dict) and str(item.get("command", "")) == command for item in block_hooks):
            block_hooks.append({"name": _GEMINI_HOOK_NAME, "type": "command", "command": command})

        hooks_config = payload.get("hooksConfig")
        if not isinstance(hooks_config, dict):
            hooks_config = {}
            payload["hooksConfig"] = hooks_config
        hooks_config["enabled"] = True
        hooks_config["notifications"] = True
        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

        after_agent = hooks.get("AfterAgent")
        if not isinstance(after_agent, list):
            return payload

        command = f"uv run cybervisor-agent-hook --config {runtime_config_path.resolve()}"
        filtered_after_agent: list[Any] = []
        for block in after_agent:
            if not isinstance(block, dict):
                filtered_after_agent.append(block)
                continue
            block_hooks = block.get("hooks")
            if not isinstance(block_hooks, list):
                filtered_after_agent.append(block)
                continue
            filtered_hooks = [
                item
                for item in block_hooks
                if not (isinstance(item, dict) and str(item.get("command", "")) == command)
            ]
            if filtered_hooks:
                updated_block = dict(block)
                updated_block["hooks"] = filtered_hooks
                filtered_after_agent.append(updated_block)

        if filtered_after_agent:
            hooks["AfterAgent"] = filtered_after_agent
        else:
            hooks.pop("AfterAgent", None)

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