"""Dashboard API endpoints for the Tools tab.

Exposes tool registry, prompt building, job queue, and execution via REST.
"""

from typing import Any

import structlog
from fastapi import APIRouter, HTTPException, Query
from pydantic import BaseModel, Field

from proxym.tools import ToolCategory, ToolRegistry

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


# ── Request models ────────────────────────────────────────

class DispatchRequest(BaseModel):
    ticket_id: str = Field(..., description="Ticket ID to dispatch")
    tool: str = Field(..., description="Tool name (aider, claude-code, ...)")
    mode: str = Field("", description="Agent mode (code, architect, debug, ask)")
    model: str = Field("", description="Model alias or full name")
    project_dir: str = Field("", description="Project directory")


class PreviewPromptRequest(BaseModel):
    ticket_id: str = Field(..., description="Ticket ID")
    tool: str = Field(..., description="Tool name")
    mode: str = Field("", description="Agent mode")


# ── Tool registry endpoints ──────────────────────────────

@router.get("/")
async def list_tools(
    category: str | None = Query(None, description="Filter by category: ide, agent_cli, browser"),
    available_only: bool = Query(False, description="Only show tools with an available host or docker-backed instance"),
) -> dict[str, Any]:
    """List all registered tools."""
    reg = ToolRegistry.get()
    cat = ToolCategory(category) if category else None
    tools = reg.list_tools(category=cat, available_only=available_only)
    return {"tools": [t.summary() for t in tools], "count": len(tools)}


@router.get("/summary")
async def tools_summary() -> dict[str, Any]:
    """Summary of tool availability."""
    return ToolRegistry.get().summary()


# ── Prompt preview ───────────────────────────────────────

@router.post("/prompt/preview")
async def preview_prompt(req: PreviewPromptRequest) -> dict[str, Any]:
    """Preview the prompt that would be sent to a tool for a ticket."""
    tool = ToolRegistry.get().get_tool(req.tool)
    if not tool:
        raise HTTPException(404, f"Tool '{req.tool}' not found")

    from proxym.tools.prompt_builder import build_prompt_dict
    ticket = _get_ticket(req.ticket_id)
    return build_prompt_dict(ticket, tool, req.mode)


# ── Job dispatch ─────────────────────────────────────────

@router.post("/dispatch")
async def dispatch_job(req: DispatchRequest) -> dict[str, Any]:
    """Dispatch a tool to work on a ticket. Enqueues a job."""
    tool = ToolRegistry.get().get_tool(req.tool)
    if not tool:
        raise HTTPException(404, f"Tool '{req.tool}' not found")

    ticket = _get_ticket(req.ticket_id)
    project_dir = req.project_dir or _default_project_dir()

    from proxym.tools.executor import ToolExecutor
    executor = ToolExecutor.get()
    # Make dispatch self-contained: if the worker isn't running yet, start it
    # before enqueueing so the job doesn't stay queued forever.
    await executor.start()
    job = executor.enqueue(ticket, tool, project_dir, req.mode, req.model)
    return {"job": job.summary(), "message": f"Job {job.id} enqueued for {tool.name}"}


# ── Job queue endpoints (sub-router to avoid /{tool_name} shadow) ──

queue_router = APIRouter(prefix="/queue", tags=["tools"])


@queue_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"] = [j.summary() for j in recent]
    return summary


@queue_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": [j.summary() for j in jobs], "count": len(jobs)}


@queue_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")
    data = job.summary()
    if job.result:
        data["result"] = {
            **job.result.summary(),
            "stdout": job.result.stdout,
            "stderr": job.result.stderr,
        }
    return data


@queue_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")


@queue_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()}


@queue_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")

    # Build diagnosis context from the job error
    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 "",
    )

    # Also provide actionable quick-fixes based on known patterns
    quick_fixes = _suggest_quick_fixes(job)
    diagnosis["quick_fixes"] = quick_fixes
    diagnosis["job_id"] = job_id
    diagnosis["tool"] = job.tool_name

    return diagnosis


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({
                "action": "install",
                "label": "Install aider",
                "command": "pip install aider-chat",
                "description": "Install aider CLI tool via pip",
            })
        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


@queue_router.post("/start")
async def start_executor() -> dict[str, Any]:
    """Start the background executor loop."""
    from proxym.tools.executor import ToolExecutor
    await ToolExecutor.get().start()
    return {"ok": True, "message": "Executor started"}


@queue_router.post("/stop")
async def stop_executor() -> dict[str, Any]:
    """Stop the background executor loop."""
    from proxym.tools.executor import ToolExecutor
    await ToolExecutor.get().stop()
    return {"ok": True, "message": "Executor stopped"}


router.include_router(queue_router)


# NOTE: Dynamic route MUST be after include_router(queue_router).
@router.get("/{tool_name}")
async def get_tool(tool_name: str) -> dict[str, Any]:
    """Get details for a specific tool."""
    tool = ToolRegistry.get().get_tool(tool_name)
    if not tool:
        raise HTTPException(404, f"Tool '{tool_name}' not found")
    return tool.summary()


# ── Helpers ──────────────────────────────────────────────

def _get_ticket(ticket_id: str):
    """Fetch a ticket by ID. Raises 404 if not found."""
    from proxym.dashboard._tickets_api import _tickets
    mgr = _tickets()
    ticket = mgr.get_ticket(ticket_id)
    if not ticket:
        raise HTTPException(404, f"Ticket '{ticket_id}' not found")
    return ticket


def _default_project_dir() -> str:
    """Return default project directory from settings."""
    try:
        from proxym.config import get_settings
        s = get_settings()
        return str(getattr(s, "projects_root", "~/github"))
    except Exception:
        return "~/github"
