"""Base adapter interface."""

from __future__ import annotations

import subprocess
import time
from abc import ABC, abstractmethod
from pathlib import Path
from typing import Optional

from coderace.cost import CostResult
from coderace.types import AgentResult


class BaseAdapter(ABC):
    """Abstract base class for coding agent adapters."""

    name: str = "base"

    def __init__(self, model: Optional[str] = None) -> None:
        """Initialize adapter with optional model override."""
        self.model = model

    @abstractmethod
    def build_command(self, task_description: str, model: Optional[str] = None) -> list[str]:
        """Build the CLI command to invoke this agent."""
        ...

    def parse_cost(
        self,
        stdout: str,
        stderr: str,
        model_name: str = "",
        custom_pricing: dict[str, tuple[float, float]] | None = None,
    ) -> Optional[CostResult]:
        """Parse cost data from agent output. Override in subclasses.

        Returns None if cost data is unavailable.
        """
        return None

    def run(
        self,
        task_description: str,
        workdir: Path,
        timeout: int,
        no_cost: bool = False,
        custom_pricing: dict[str, tuple[float, float]] | None = None,
    ) -> AgentResult:
        """Run the agent on a task and capture results."""
        model = self.model
        cmd = self.build_command(task_description, model=model)
        start = time.monotonic()
        timed_out = False

        try:
            proc = subprocess.run(
                cmd,
                cwd=workdir,
                capture_output=True,
                text=True,
                timeout=timeout,
            )
            exit_code = proc.returncode
            stdout = proc.stdout
            stderr = proc.stderr
        except subprocess.TimeoutExpired as e:
            timed_out = True
            exit_code = -1
            stdout = (e.stdout or b"").decode("utf-8", errors="replace") if e.stdout else ""
            stderr = (e.stderr or b"").decode("utf-8", errors="replace") if e.stderr else ""
        except FileNotFoundError:
            timed_out = False
            exit_code = 127
            stdout = ""
            stderr = f"Command not found: {cmd[0]}"

        wall_time = time.monotonic() - start

        # Parse cost (fails gracefully — never raises)
        cost_result: Optional[CostResult] = None
        if not no_cost:
            try:
                cost_result = self.parse_cost(
                    stdout,
                    stderr,
                    model_name=model or "",
                    custom_pricing=custom_pricing,
                )
            except Exception:
                pass

        return AgentResult(
            agent=self.name,
            exit_code=exit_code,
            stdout=stdout,
            stderr=stderr,
            wall_time=wall_time,
            timed_out=timed_out,
            cost_result=cost_result,
        )
