"""Job queue endpoints: status, list, get, cancel, retry, repair."""

from typing import Any

import structlog
from fastapi import APIRouter, HTTPException, Query

from proxym.tools import ToolRegistry
from proxym.dashboard.tools._helpers import _get_ticket, _job_snapshot

logger = structlog.get_logger()
router = APIRouter(prefix="/queue", tags=["tools"])


@router.get("")
async def queue_status() -> dict[str, Any]:
    """Get queue summary and recent jobs."""
    from proxym.tools.executor import ToolExecutor
    executor = ToolExecutor.get()
    summary = executor.queue_summary()
    recent = executor.list_jobs(limit=20)
    summary["recent_jobs"] = [_job_snapshot(j) for j in recent]
    return summary


@router.get("/jobs")
async def list_jobs(
    status: str | None = Query(None, description="Filter: queued, running, done, failed, cancelled"),
    limit: int = Query(50, ge=1, le=200),
) -> dict[str, Any]:
    """List jobs with optional status filter."""
    from proxym.tools.executor import JobStatus, ToolExecutor
    executor = ToolExecutor.get()
    st = JobStatus(status) if status else None
    jobs = executor.list_jobs(status=st, limit=limit)
    return {"jobs": [_job_snapshot(j) for j in jobs], "count": len(jobs)}


@router.get("/jobs/{job_id}")
async def get_job(job_id: str) -> dict[str, Any]:
    """Get details for a specific job."""
    from proxym.tools.executor import ToolExecutor
    job = ToolExecutor.get().get_job(job_id)
    if not job:
        raise HTTPException(404, f"Job '{job_id}' not found")
    return _job_snapshot(job)


@router.post("/jobs/{job_id}/cancel")
async def cancel_job(job_id: str) -> dict[str, Any]:
    """Cancel a queued job."""
    from proxym.tools.executor import ToolExecutor
    if ToolExecutor.get().cancel_job(job_id):
        return {"ok": True, "job_id": job_id}
    raise HTTPException(400, "Job not found or not in queued state")


@router.post("/jobs/{job_id}/retry")
async def retry_job(job_id: str) -> dict[str, Any]:
    """Retry a failed job — re-enqueue with same parameters."""
    from proxym.tools.executor import JobStatus, ToolExecutor
    executor = ToolExecutor.get()
    old_job = executor.get_job(job_id)
    if not old_job:
        raise HTTPException(404, f"Job '{job_id}' not found")
    if old_job.status not in (JobStatus.FAILED, JobStatus.CANCELLED):
        raise HTTPException(400, "Only failed or cancelled jobs can be retried")

    ticket = _get_ticket(old_job.ticket_id)
    tool = ToolRegistry.get().get_tool(old_job.tool_name)
    if not tool:
        raise HTTPException(404, f"Tool '{old_job.tool_name}' no longer registered")

    await executor.start()
    new_job = executor.enqueue(ticket, tool, old_job.project_dir, old_job.mode, old_job.model)
    return {"ok": True, "old_job_id": job_id, "new_job": new_job.summary()}


@router.post("/jobs/{job_id}/repair")
async def repair_job(job_id: str) -> dict[str, Any]:
    """Run auto-diagnosis on a failed job and suggest/apply fixes."""
    from proxym.tools.executor import JobStatus, ToolExecutor
    executor = ToolExecutor.get()
    job = executor.get_job(job_id)
    if not job:
        raise HTTPException(404, f"Job '{job_id}' not found")
    if job.status != JobStatus.FAILED:
        raise HTTPException(400, "Only failed jobs can be repaired")

    quick_fixes = _suggest_quick_fixes(job)
    try:
        from proxym.observability.repair_agent import RepairAgent
        agent = RepairAgent(mode="suggest")
        diagnosis = await agent.diagnose_and_repair(
            error_text=job.error,
            traceback_text=job.result.stderr if job.result else "",
        )
    except Exception as exc:
        diagnosis = {"error": f"Repair diagnosis failed: {exc}"}

    return _finalize_repair_diagnosis(job_id, job, quick_fixes, diagnosis)


# ── Repair helpers ────────────────────────────────────────

from proxym.dashboard.tools.utils._aider_install_fix import _aider_install_fix


def _suggest_quick_fixes(job) -> list[dict[str, str]]:
    """Return actionable fix suggestions for common job failures."""
    fixes: list[dict[str, str]] = []
    error = (job.error or "").lower()
    tool = job.tool_name

    if "not found on path" in error or "no such file or directory" in error:
        if tool == "aider":
            fixes.append(_aider_install_fix())
        elif tool == "vscode":
            fixes.append({
                "action": "start_container",
                "label": "Start VS Code container",
                "command": "docker compose --profile vscode up -d",
                "description": "Start VS Code Web via Docker Compose",
            })
        elif tool in ("windsurf", "cursor"):
            fixes.append({
                "action": "info",
                "label": f"Install {tool}",
                "command": f"# {tool} is a desktop IDE — install from https://{tool}.com",
                "description": f"{tool} is not available as CLI inside containers. Use from host machine.",
            })
        elif tool == "claude-code":
            fixes.append({
                "action": "install",
                "label": "Install Claude Code",
                "command": "npm install -g @anthropic-ai/claude-code",
                "description": "Install Claude Code CLI via npm",
            })
        else:
            fixes.append({
                "action": "info",
                "label": f"Install {tool}",
                "command": f"# Install {tool} binary and ensure it's on PATH",
                "description": f"Binary for {tool} not found in container",
            })

    if "docker" in error:
        fixes.append({
            "action": "info",
            "label": "Docker not available",
            "command": "# Docker socket not mounted in container. Run from host.",
            "description": "This tool requires Docker, which is not available inside the proxym container.",
        })

    if "timeout" in error:
        fixes.append({
            "action": "retry",
            "label": "Retry with longer timeout",
            "command": "",
            "description": "The tool timed out. Try dispatching again — the service may have been starting up.",
        })

    if "queue full" in error:
        fixes.append({
            "action": "retry",
            "label": "Retry later",
            "command": "",
            "description": "Job queue was full. Wait for current jobs to finish and retry.",
        })

    if not fixes:
        fixes.append({
            "action": "diagnose",
            "label": "Run LLM diagnosis",
            "command": "",
            "description": "No known quick fix. Click to run AI-powered diagnosis.",
        })

    return fixes


def _finalize_repair_diagnosis(
    job_id: str,
    job,
    quick_fixes: list[dict[str, str]],
    diagnosis: Any,
) -> dict[str, Any]:
    """Normalize repair agent output and fill in local fallback guidance."""
    normalized = diagnosis if isinstance(diagnosis, dict) else {}
    normalized.setdefault("diagnosis", "")
    normalized.setdefault("suggestion", "")

    if not normalized.get("diagnosis") and not normalized.get("suggestion"):
        if quick_fixes:
            normalized["diagnosis"] = f"Detected a likely environment issue from the job error: {job.error}"
            normalized["suggestion"] = quick_fixes[0].get("description", quick_fixes[0].get("label", ""))
        else:
            normalized["diagnosis"] = f"No known fix found for job error: {job.error}"
            normalized["suggestion"] = "Run a manual diagnosis or retry the job after checking the environment."

    normalized["quick_fixes"] = quick_fixes
    normalized["job_id"] = job_id
    normalized["tool"] = job.tool_name
    normalized["job_error"] = job.error
    return normalized
