"""
Ejecutor de tareas del Agent CLI.

Recibe tareas del cloud vía WebSocket, las ejecuta como subprocess
con timeout configurable y captura stdout/stderr.
"""
import asyncio
import shlex
import time
from dataclasses import dataclass, field
from typing import Any

from jama_agent.logger import log


@dataclass
class TaskResult:
    """Resultado de una tarea ejecutada."""

    task_id: str
    status: str = "completed"  # completed, error, timeout
    output: str = ""
    stderr: str = ""
    exit_code: int = 0
    duration_ms: int = 0
    metadata: dict[str, Any] = field(default_factory=dict)


def _build_command(tool: str, args: dict[str, Any]) -> str:
    """Construye el comando shell a partir de tool + args.

    Args:
        tool: Nombre de la herramienta (ej: "nmap", "nuclei")
        args: Diccionario de argumentos

    Returns:
        Comando shell como string
    """
    parts = [tool]

    for key, value in args.items():
        # Normalizar nombre del flag
        if len(key) == 1:
            flag = f"-{key}"
        else:
            flag = f"--{key}" if not key.startswith("-") else key

        if isinstance(value, bool):
            if value:
                parts.append(flag)
        elif isinstance(value, list):
            for item in value:
                parts.append(flag)
                parts.append(str(item))
        elif value is not None:
            parts.append(flag)
            parts.append(str(value))

    return " ".join(parts)


async def execute_task(
    task_id: str,
    tool: str,
    args: dict[str, Any],
    timeout: int = 300,
) -> TaskResult:
    """Ejecuta una herramienta como subprocess asíncrono.

    Args:
        task_id: ID único de la tarea
        tool: Nombre de la herramienta
        args: Argumentos de la herramienta
        timeout: Timeout en segundos

    Returns:
        TaskResult con el resultado de la ejecución
    """
    command = _build_command(tool, args)
    log.info(f"Ejecutando tarea {task_id}: {command}")

    start_time = time.monotonic()

    try:
        process = await asyncio.create_subprocess_shell(
            command,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE,
        )

        try:
            stdout_bytes, stderr_bytes = await asyncio.wait_for(
                process.communicate(),
                timeout=timeout,
            )
        except asyncio.TimeoutError:
            process.kill()
            await process.wait()
            elapsed = int((time.monotonic() - start_time) * 1000)
            log.warning(f"Tarea {task_id} timeout después de {timeout}s")
            return TaskResult(
                task_id=task_id,
                status="timeout",
                output=f"Task timed out after {timeout}s",
                exit_code=-1,
                duration_ms=elapsed,
            )

        elapsed = int((time.monotonic() - start_time) * 1000)
        stdout = stdout_bytes.decode("utf-8", errors="replace") if stdout_bytes else ""
        stderr = stderr_bytes.decode("utf-8", errors="replace") if stderr_bytes else ""
        exit_code = process.returncode or 0

        status = "completed" if exit_code == 0 else "error"
        log.info(
            f"Tarea {task_id} {status} (exit={exit_code}, {elapsed}ms)"
        )

        return TaskResult(
            task_id=task_id,
            status=status,
            output=stdout,
            stderr=stderr,
            exit_code=exit_code,
            duration_ms=elapsed,
        )

    except FileNotFoundError:
        elapsed = int((time.monotonic() - start_time) * 1000)
        log.error(f"Herramienta '{tool}' no encontrada")
        return TaskResult(
            task_id=task_id,
            status="error",
            output=f"Tool '{tool}' not found on this system",
            exit_code=-1,
            duration_ms=elapsed,
        )
    except Exception as exc:
        elapsed = int((time.monotonic() - start_time) * 1000)
        log.error(f"Error ejecutando tarea {task_id}: {exc}")
        return TaskResult(
            task_id=task_id,
            status="error",
            output=str(exc),
            exit_code=-1,
            duration_ms=elapsed,
        )
