"""GitHub Copilot CLI agent adapter."""

from __future__ import annotations

import json
import logging
import subprocess

from codeprobe.adapters._base import BaseAdapter
from codeprobe.adapters.protocol import AgentConfig, AgentOutput
from codeprobe.adapters.telemetry import NdjsonStreamCollector

logger = logging.getLogger(__name__)


class CopilotAdapter(BaseAdapter):
    """Adapter for GitHub Copilot CLI."""

    _binary_name = "copilot"
    _install_hint = (
        "Copilot CLI not found. Install from https://github.com/github/copilot-cli"
    )

    def __init__(self) -> None:
        self._collector = NdjsonStreamCollector()

    def preflight(self, config: AgentConfig) -> list[str]:
        return super().preflight(config)

    def build_command(self, prompt: str, config: AgentConfig) -> list[str]:
        binary = self._require_binary()
        cmd = [binary, "--prompt", prompt, "--output-format", "json"]

        # Non-interactive mode requires --allow-all-tools for tool auto-approval
        cmd.append("--allow-all-tools")

        if config.model:
            cmd.extend(["--model", config.model])

        mcp_path = self._write_mcp_config(config)
        if mcp_path:
            cmd.extend(["--additional-mcp-config", f"@{mcp_path}"])

        return cmd

    def parse_output(
        self, result: subprocess.CompletedProcess[str], duration: float
    ) -> AgentOutput:
        """Parse Copilot CLI NDJSON output for token data.

        Requires Copilot CLI 1.0.4+ with --output-format json which emits
        NDJSON lines containing "assistant.message" events with outputTokens.

        Input tokens are extracted from NDJSON ``usage`` events when available,
        falling back to Copilot process log parsing.
        """
        raw = result.stdout or ""
        usage = self._collector.collect(raw)

        # Extract content text from NDJSON events.
        # On JSON parse failure, the except clause resets to empty,
        # and the fallback below uses raw output — matching original behavior.
        result_text_parts: list[str] = []
        try:
            for line in raw.strip().splitlines():
                if not line.strip():
                    continue
                obj = json.loads(line)
                event_type = obj.get("type", "")
                if event_type == "assistant.message":
                    content = obj.get("data", {}).get("content", "")
                    if content:
                        result_text_parts.append(content)
                elif event_type == "result":
                    content = obj.get("data", {}).get("content", "")
                    if content:
                        result_text_parts.append(content)
        except json.JSONDecodeError:
            logger.warning(
                "ndjson_parse_fallback: failed to parse Copilot NDJSON output, "
                "falling back to raw stdout"
            )
            result_text_parts = []
        stdout_text = "\n".join(result_text_parts) if result_text_parts else raw

        # When NDJSON parsing fell back to raw output, surface the fallback
        # in the error field so callers can detect degraded telemetry.
        error = usage.error
        if not result_text_parts and raw:
            fallback_msg = "ndjson_parse_fallback: raw stdout used as output"
            error = f"{error}; {fallback_msg}" if error else fallback_msg

        return AgentOutput(
            stdout=stdout_text,
            stderr=result.stderr or None,
            exit_code=result.returncode,
            duration_seconds=duration,
            input_tokens=usage.input_tokens,
            output_tokens=usage.output_tokens,
            cost_usd=usage.cost_usd,
            cost_model=usage.cost_model,
            cost_source=usage.cost_source,
            error=error,
        )
