"""Test command and test run REST API routes."""

import asyncio

from fastapi import APIRouter, Depends, HTTPException, Query

from peon_mcp.common.dependencies import get_db
from peon_mcp.common.schemas import PaginatedResponse, SuccessResponse
from peon_mcp.db import row_to_dict
from peon_mcp.testing.execution import execute_tests_for_task
from peon_mcp.webhooks.engine import EventType, emit_event
from peon_mcp.testing.schemas import (
    CreateTestCommandRequest,
    TestCommandResponse,
    TestRunResponse,
    UpdateTestCommandRequest,
)

router = APIRouter()


# ---------------------------------------------------------------------------
# Test command endpoints
# ---------------------------------------------------------------------------


@router.get("/api/projects/{project_id}/test-commands", response_model=list[TestCommandResponse], tags=["Test Commands"])
async def list_test_commands(project_id: str, db=Depends(get_db)):
    """List test commands for a project ordered by sort_order."""
    rows = await db.execute_fetchall(
        "SELECT * FROM test_commands WHERE project_id = ? ORDER BY sort_order ASC",
        (project_id,),
    )
    return [row_to_dict(r) for r in rows]


@router.post("/api/projects/{project_id}/test-commands", response_model=TestCommandResponse, status_code=201, tags=["Test Commands"])
async def create_test_command(project_id: str, body: CreateTestCommandRequest, db=Depends(get_db)):
    """Create a test command for a project."""
    name = body.name.strip()
    command = body.command.strip()

    if not name or not command:
        raise HTTPException(400, detail="name and command are required")

    cursor = await db.execute(
        """INSERT INTO test_commands
           (project_id, name, command, timeout_seconds, sort_order, report_path, max_retries, enabled)
           VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
        (project_id, name, command, body.timeout_seconds, body.sort_order, body.report_path, body.max_retries, body.enabled),
    )
    await db.commit()

    rows = await db.execute_fetchall(
        "SELECT * FROM test_commands WHERE id = ?", (cursor.lastrowid,)
    )
    return row_to_dict(rows[0])


@router.put("/api/test-commands/{command_id}", response_model=TestCommandResponse, tags=["Test Commands"])
async def update_test_command(command_id: int, body: UpdateTestCommandRequest, db=Depends(get_db)):
    """Update a test command."""
    provided = body.model_dump(exclude_unset=True)

    fields: list[str] = []
    params: list = []
    for key in ("name", "command", "timeout_seconds", "sort_order", "report_path", "max_retries", "enabled"):
        if key in provided:
            fields.append(f"{key} = ?")
            params.append(provided[key])

    if not fields:
        raise HTTPException(400, detail="No fields to update")

    fields.append("updated_at = CURRENT_TIMESTAMP")
    params.append(command_id)

    await db.execute(
        f"UPDATE test_commands SET {', '.join(fields)} WHERE id = ?", params
    )
    await db.commit()

    rows = await db.execute_fetchall(
        "SELECT * FROM test_commands WHERE id = ?", (command_id,)
    )
    if not rows:
        raise HTTPException(404, detail="Test command not found")

    return row_to_dict(rows[0])


@router.delete("/api/test-commands/{command_id}", response_model=SuccessResponse, tags=["Test Commands"])
async def delete_test_command(command_id: int, db=Depends(get_db)):
    """Delete a test command."""
    await db.execute("DELETE FROM test_commands WHERE id = ?", (command_id,))
    await db.commit()
    return {"success": True}


# ---------------------------------------------------------------------------
# Test run endpoints
# ---------------------------------------------------------------------------


@router.get("/api/projects/{project_id}/test-runs", response_model=PaginatedResponse[TestRunResponse], tags=["Test Runs"])
async def list_project_test_runs(
    project_id: str,
    limit: int = Query(default=50, ge=1, le=200),
    offset: int = Query(default=0, ge=0),
    db=Depends(get_db),
):
    """List test runs for a project with pagination."""
    # Get total count for pagination metadata
    count_rows = await db.execute_fetchall(
        "SELECT COUNT(*) FROM test_runs WHERE project_id = ?",
        (project_id,),
    )
    total = count_rows[0][0] if count_rows else 0

    rows = await db.execute_fetchall(
        "SELECT * FROM test_runs WHERE project_id = ? ORDER BY created_at DESC LIMIT ? OFFSET ?",
        (project_id, limit, offset),
    )
    items = [row_to_dict(r) for r in rows]

    return {
        "items": items,
        "total": total,
        "limit": limit,
        "offset": offset,
        "has_next": (offset + len(items)) < total,
    }


@router.get("/api/tasks/{task_id}/test-runs", response_model=list[TestRunResponse], tags=["Test Runs"])
async def list_task_test_runs(task_id: int, db=Depends(get_db)):
    """List test runs for a task, including nested test_cases."""
    # Get test runs for this task
    run_rows = await db.execute_fetchall(
        "SELECT * FROM test_runs WHERE task_id = ? ORDER BY created_at DESC",
        (task_id,),
    )

    test_runs = []
    for run_row in run_rows:
        run_dict = row_to_dict(run_row)

        # Get nested test_cases for this run
        case_rows = await db.execute_fetchall(
            "SELECT * FROM test_cases WHERE test_run_id = ? ORDER BY id ASC",
            (run_row["id"],),
        )
        run_dict["test_cases"] = [row_to_dict(c) for c in case_rows]
        test_runs.append(run_dict)

    return test_runs


@router.post("/api/tasks/{task_id}/run-tests", tags=["Test Runs"])
async def run_tests_endpoint(task_id: int, db=Depends(get_db)):
    """Trigger test execution for a task from the UI."""
    # Look up task's project_id and worktree_path
    rows = await db.execute_fetchall(
        "SELECT project_id, worktree_path FROM tasks WHERE id = ?",
        (task_id,)
    )
    if not rows:
        raise HTTPException(404, detail="Task not found")

    project_id = rows[0]["project_id"]
    worktree_path = rows[0]["worktree_path"]

    if not worktree_path:
        raise HTTPException(400, detail="Task has no worktree_path set")

    # Execute tests using shared logic
    result = await execute_tests_for_task(db, task_id, project_id)

    # Fire-and-forget event emission based on test outcome
    overall_status = result.get("overall_status", "")
    needs_human_review = result.get("needs_human_review", False)
    event_payload = {"task_id": task_id, "project_id": project_id, **result}
    if needs_human_review:
        asyncio.create_task(emit_event(db, project_id, EventType.TEST_MAX_RETRIES_EXHAUSTED, event_payload))
    elif overall_status == "pass":
        asyncio.create_task(emit_event(db, project_id, EventType.TEST_PASSED, event_payload))
    elif overall_status == "fail":
        asyncio.create_task(emit_event(db, project_id, EventType.TEST_FAILED, event_payload))

    return result
