"""CLI backend registry.

Each backend knows how to build a command-line invocation for a
specific AI CLI tool.  To add a new backend (gemini, opencode,
tarmina, …), subclass ``BackendSpec``, implement ``build_cmd``,
and register an instance in ``BACKENDS``.
"""

from __future__ import annotations

from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from pathlib import Path


@dataclass(frozen=True, slots=True)
class BackendSpec(ABC):
    """How to invoke a specific CLI backend."""

    name: str

    @abstractmethod
    def build_cmd(
        self,
        *,
        cli: str,
        model: str,
        prompt: str,
        repo_dir: Path,
        plugin_dir: Path | None = None,
    ) -> tuple[list[str], str | None]:
        """Return ``(argv, cwd_or_None)``."""

    def build_edit_cmd(
        self,
        *,
        cli: str,
        model: str,
        prompt: str,
        repo_dir: Path,
        plugin_dir: Path | None = None,
        agent: str = "codeflash-ci",
    ) -> tuple[list[str], str | None]:
        """Return ``(argv, cwd_or_None)`` with autonomous edit permissions.

        Default falls back to ``build_cmd``; override for backends
        that need extra flags to enable file editing.
        """
        return self.build_cmd(
            cli=cli,
            model=model,
            prompt=prompt,
            repo_dir=repo_dir,
            plugin_dir=plugin_dir,
        )


@dataclass(frozen=True, slots=True)
class ClaudeBackend(BackendSpec):
    """Claude Code CLI backend."""

    def build_cmd(
        self,
        *,
        cli: str,
        model: str,
        prompt: str,
        repo_dir: Path,
        plugin_dir: Path | None = None,
    ) -> tuple[list[str], str | None]:
        cmd = [cli, "-p", prompt, "--model", model]
        if plugin_dir:
            cmd += ["--plugin-dir", str(plugin_dir)]
        return cmd, str(repo_dir)

    def build_edit_cmd(
        self,
        *,
        cli: str,
        model: str,
        prompt: str,
        repo_dir: Path,
        plugin_dir: Path | None = None,
        agent: str = "codeflash-ci",
    ) -> tuple[list[str], str | None]:
        cmd = [
            cli,
            "-p",
            prompt,
            "--model",
            model,
            "--agent",
            agent,
            "--max-turns",
            "200",
            "--dangerously-skip-permissions",
        ]
        if plugin_dir:
            cmd += ["--plugin-dir", str(plugin_dir)]
        return cmd, str(repo_dir)


@dataclass(frozen=True, slots=True)
class CodexBackend(BackendSpec):
    """OpenAI Codex CLI backend."""

    def build_cmd(
        self,
        *,
        cli: str,
        model: str,
        prompt: str,
        repo_dir: Path,
        plugin_dir: Path | None = None,
    ) -> tuple[list[str], str | None]:
        cmd = [
            cli,
            "exec",
            "--model",
            model,
            "--full-auto",
            "-C",
            str(repo_dir),
            "-o",
            "/dev/stdout",
            prompt,
        ]
        return cmd, None


BACKENDS: dict[str, BackendSpec] = {
    "claude": ClaudeBackend(name="claude"),
    "codex": CodexBackend(name="codex"),
}


def get_backend(name: str) -> BackendSpec:
    """Look up a backend by name.

    Raises ``ValueError`` for unknown backends.
    """
    if name not in BACKENDS:
        known = ", ".join(sorted(BACKENDS))
        msg = f"Unknown backend {name!r}. Known: {known}"
        raise ValueError(msg)
    return BACKENDS[name]
