from __future__ import annotations

import subprocess
from dataclasses import dataclass
from typing import Any, Protocol

from .sessions import utc_now


@dataclass
class AgentResult:
    events: list[dict[str, Any]]
    avatar_state: str = "speaking"


class Adapter(Protocol):
    def run(self, message: str, agent: dict[str, Any], session_id: str) -> AgentResult:
        ...


class EchoAdapter:
    def run(self, message: str, agent: dict[str, Any], session_id: str) -> AgentResult:
        return AgentResult(
            events=[
                {
                    "type": "status",
                    "agent": agent["id"],
                    "content": "EchoAdapter actif, aucun LLM appelé.",
                    "created_at": utc_now(),
                },
                {
                    "type": "assistant_message",
                    "agent": agent["id"],
                    "content": f"Echo: {message}",
                    "raw": {"adapter": "echo"},
                    "created_at": utc_now(),
                },
            ]
        )


class CliAdapter:
    def run(self, message: str, agent: dict[str, Any], session_id: str) -> AgentResult:
        command = agent.get("command")
        if not isinstance(command, list) or not command:
            return AgentResult(
                avatar_state="error",
                events=[self._error(agent, "Commande CLI absente ou invalide.")],
            )

        argv = [str(part).replace("{prompt}", message).replace("{session_id}", session_id) for part in command]
        timeout = int(agent.get("timeout_seconds", 300))

        status_event = {
            "type": "status",
            "agent": agent["id"],
            "content": "Lancement commande: " + " ".join(argv[:3]) + (" ..." if len(argv) > 3 else ""),
            "raw": {"argv": argv, "timeout_seconds": timeout},
            "created_at": utc_now(),
        }

        try:
            completed = subprocess.run(
                argv,
                text=True,
                capture_output=True,
                timeout=timeout,
                check=False,
            )
        except FileNotFoundError as exc:
            return AgentResult(avatar_state="error", events=[status_event, self._error(agent, str(exc))])
        except subprocess.TimeoutExpired:
            return AgentResult(avatar_state="error", events=[status_event, self._error(agent, "Timeout de l'agent CLI.")])

        output = completed.stdout.strip()
        stderr = completed.stderr.strip()
        if completed.returncode != 0:
            return AgentResult(
                avatar_state="error",
                events=[
                    status_event,
                    self._error(
                        agent,
                        stderr or output or f"Commande terminée avec code {completed.returncode}",
                        raw={"returncode": completed.returncode, "stdout": output, "stderr": stderr},
                    ),
                ],
            )

        return AgentResult(
            events=[
                status_event,
                {
                    "type": "assistant_message",
                    "agent": agent["id"],
                    "content": output or "[commande terminée sans sortie]",
                    "raw": {"returncode": completed.returncode, "stderr": stderr},
                    "created_at": utc_now(),
                },
            ]
        )

    @staticmethod
    def _error(agent: dict[str, Any], content: str, raw: dict[str, Any] | None = None) -> dict[str, Any]:
        return {
            "type": "error",
            "agent": agent.get("id", "unknown"),
            "content": content,
            "raw": raw or {},
            "created_at": utc_now(),
        }


def adapter_for(agent: dict[str, Any]) -> Adapter:
    kind = agent.get("type", "echo")
    if kind == "echo":
        return EchoAdapter()
    if kind == "cli":
        return CliAdapter()
    raise ValueError(f"Type d'adaptateur non supporté: {kind}")
