"""Project REST API routes."""

import json
from datetime import datetime, timedelta, timezone
from typing import Any

import aiosqlite
from fastapi import APIRouter, Depends, HTTPException, Query

from peon_mcp.common.dependencies import get_db
from peon_mcp.common.schemas import PaginatedResponse
from peon_mcp.db import row_to_dict
from peon_mcp.projects.schemas import (
    ActivityDataPoint,
    ContextPruningConfig,
    CreateProjectRequest,
    ProjectActivityResponse,
    ProjectResponse,
    ProjectStatsResponse,
    UpdateProjectRequest,
)

router = APIRouter(tags=["Projects"])


@router.get("/api/projects", response_model=list[ProjectResponse])
async def list_projects(db=Depends(get_db)):
    rows = await db.execute_fetchall(
        "SELECT * FROM projects ORDER BY created_at DESC"
    )
    return [row_to_dict(r) for r in rows]


@router.post("/api/projects", response_model=ProjectResponse, status_code=201)
async def create_project(body: CreateProjectRequest, db=Depends(get_db)):
    project_id = body.id.strip()
    path = body.path.strip()
    if not project_id or not path:
        raise HTTPException(400, detail="id and path are required")
    try:
        await db.execute(
            "INSERT INTO projects (id, path, github_url) VALUES (?, ?, ?)",
            (project_id, path, body.github_url.strip()),
        )
        await db.commit()
        rows = await db.execute_fetchall(
            "SELECT * FROM projects WHERE id = ?", (project_id,)
        )
        return row_to_dict(rows[0])
    except aiosqlite.IntegrityError:
        raise HTTPException(409, detail=f"Project '{project_id}' already exists")


@router.put("/api/projects/{project_id}", response_model=ProjectResponse)
async def update_project(project_id: str, body: UpdateProjectRequest, db=Depends(get_db)):
    rows = await db.execute_fetchall(
        "SELECT * FROM projects WHERE id = ?", (project_id,)
    )
    if not rows:
        raise HTTPException(404, detail=f"Project '{project_id}' not found")

    fields: list[str] = []
    params: list = []
    if body.path is not None:
        fields.append("path = ?")
        params.append(body.path.strip())
    if body.github_url is not None:
        fields.append("github_url = ?")
        params.append(body.github_url.strip())

    if not fields:
        return row_to_dict(rows[0])

    params.append(project_id)
    await db.execute(
        f"UPDATE projects SET {', '.join(fields)} WHERE id = ?",
        params,
    )
    await db.commit()
    rows = await db.execute_fetchall(
        "SELECT * FROM projects WHERE id = ?", (project_id,)
    )
    return row_to_dict(rows[0])


@router.get("/api/projects/{project_id}/stats", response_model=ProjectStatsResponse)
async def project_stats(project_id: str, db=Depends(get_db)):
    # Feature counts by status
    rows = await db.execute_fetchall(
        "SELECT status, COUNT(*) as count FROM features WHERE project_id = ? GROUP BY status",
        (project_id,),
    )
    feature_counts = {r["status"]: r["count"] for r in rows}

    # Task counts by status
    rows = await db.execute_fetchall(
        "SELECT status, COUNT(*) as count FROM tasks WHERE project_id = ? GROUP BY status",
        (project_id,),
    )
    task_counts = {r["status"]: r["count"] for r in rows}

    # Task counts by priority
    rows = await db.execute_fetchall(
        "SELECT priority, COUNT(*) as count FROM tasks WHERE project_id = ? GROUP BY priority",
        (project_id,),
    )
    task_priority_counts = {r["priority"]: r["count"] for r in rows}

    # Work log stats
    rows = await db.execute_fetchall(
        "SELECT COUNT(*) as total FROM work_logs WHERE project_id = ?",
        (project_id,),
    )
    total_logs = rows[0]["total"]

    rows = await db.execute_fetchall(
        "SELECT test_result, COUNT(*) as count FROM work_logs WHERE project_id = ? AND test_result != '' GROUP BY test_result",
        (project_id,),
    )
    test_counts = {r["test_result"]: r["count"] for r in rows}

    # Recent logs
    rows = await db.execute_fetchall(
        "SELECT * FROM work_logs WHERE project_id = ? ORDER BY created_at DESC LIMIT 10",
        (project_id,),
    )
    recent_logs = [row_to_dict(r) for r in rows]

    # Task duration statistics (for completed tasks with both timestamps)
    rows = await db.execute_fetchall(
        """
        SELECT
            AVG((julianday(completed_at) - julianday(started_at)) * 86400) as avg_duration,
            MIN((julianday(completed_at) - julianday(started_at)) * 86400) as min_duration,
            MAX((julianday(completed_at) - julianday(started_at)) * 86400) as max_duration,
            COUNT(*) as completed_count
        FROM tasks
        WHERE project_id = ?
        AND started_at IS NOT NULL
        AND completed_at IS NOT NULL
        """,
        (project_id,),
    )
    duration_stats = {
        "avg_duration": rows[0]["avg_duration"] if rows[0]["avg_duration"] else None,
        "min_duration": rows[0]["min_duration"] if rows[0]["min_duration"] else None,
        "max_duration": rows[0]["max_duration"] if rows[0]["max_duration"] else None,
        "completed_count": rows[0]["completed_count"] or 0,
    }

    # Performance metrics aggregation (lines added/deleted, tokens used)
    rows = await db.execute_fetchall(
        """
        SELECT
            SUM(lines_added) as total_lines_added,
            SUM(lines_deleted) as total_lines_deleted,
            SUM(tokens_used) as total_tokens_used
        FROM tasks
        WHERE project_id = ?
        """,
        (project_id,),
    )
    performance_stats = {
        "total_lines_added": rows[0]["total_lines_added"] or 0,
        "total_lines_deleted": rows[0]["total_lines_deleted"] or 0,
        "total_tokens_used": rows[0]["total_tokens_used"] or 0,
    }

    # Test run statistics (only if test commands are configured)
    test_run_stats = None
    rows = await db.execute_fetchall(
        "SELECT COUNT(*) as count FROM test_commands WHERE project_id = ? AND enabled = 1",
        (project_id,),
    )
    configured_commands = rows[0]["count"] if rows else 0

    if configured_commands > 0:
        # Aggregate test runs by status with count and average duration
        rows = await db.execute_fetchall(
            """
            SELECT
                status,
                COUNT(*) as count,
                AVG(duration_seconds) as avg_duration
            FROM test_runs
            WHERE project_id = ?
              AND status IN ('pass', 'fail', 'error', 'timeout')
            GROUP BY status
            """,
            (project_id,),
        )
        by_status = {}
        total_runs = 0
        pass_count = 0
        for row in rows:
            status = row["status"]
            count = row["count"]
            avg_duration = row["avg_duration"] or 0.0
            by_status[status] = {
                "count": count,
                "avg_duration": round(avg_duration, 2)
            }
            total_runs += count
            if status == "pass":
                pass_count = count

        # Calculate pass rate
        pass_rate = (pass_count / total_runs) if total_runs > 0 else 0.0

        # Total test cases and failures
        rows = await db.execute_fetchall(
            """
            SELECT COUNT(*) as total_test_cases
            FROM test_cases tc
            JOIN test_runs tr ON tc.test_run_id = tr.id
            WHERE tr.project_id = ?
            """,
            (project_id,),
        )
        total_test_cases = rows[0]["total_test_cases"] if rows else 0

        rows = await db.execute_fetchall(
            """
            SELECT COUNT(*) as total_case_failures
            FROM test_cases tc
            JOIN test_runs tr ON tc.test_run_id = tr.id
            WHERE tr.project_id = ?
              AND tc.status IN ('fail', 'error')
            """,
            (project_id,),
        )
        total_case_failures = rows[0]["total_case_failures"] if rows else 0

        test_run_stats = {
            "by_status": by_status,
            "configured_commands": configured_commands,
            "total_runs": total_runs,
            "pass_rate": round(pass_rate, 2),
            "total_test_cases": total_test_cases,
            "total_case_failures": total_case_failures,
        }

    response_data: dict[str, Any] = {
        "feature_counts": feature_counts,
        "task_counts": task_counts,
        "task_priority_counts": task_priority_counts,
        "total_logs": total_logs,
        "test_counts": test_counts,
        "recent_logs": recent_logs,
        "duration_stats": duration_stats,
        "performance_stats": performance_stats,
    }

    # Only include test_run_stats if configured
    if test_run_stats is not None:
        response_data["test_run_stats"] = test_run_stats

    return response_data


@router.get("/api/projects/{project_id}/activity", response_model=ProjectActivityResponse)
async def project_activity(
    project_id: str,
    hours: int = Query(default=8, ge=1, le=720),
    db=Depends(get_db),
):
    # Get task completions grouped by hour for the last N hours
    rows = await db.execute_fetchall(
        """
        SELECT
            strftime('%Y-%m-%d %H:00:00', completed_at) as hour,
            COUNT(*) as count
        FROM tasks
        WHERE project_id = ?
        AND completed_at IS NOT NULL
        AND completed_at >= datetime('now', '-' || ? || ' hours')
        GROUP BY hour
        ORDER BY hour ASC
        """,
        (project_id, hours),
    )

    # Build a complete time series with zeros for hours with no completions
    now = datetime.now(timezone.utc)
    activity_data = []

    # Create a dict for quick lookup
    completion_counts = {r["hour"]: r["count"] for r in rows}

    # Generate all hours in the range
    for i in range(hours, -1, -1):
        hour_time = now - timedelta(hours=i)
        hour_str = hour_time.strftime('%Y-%m-%d %H:00:00')
        activity_data.append({
            "hour": hour_str,
            "count": completion_counts.get(hour_str, 0),
        })

    return {"activity": activity_data}


@router.get("/api/projects/{project_id}", response_model=ProjectResponse)
async def get_project(project_id: str, db=Depends(get_db)):
    rows = await db.execute_fetchall(
        "SELECT * FROM projects WHERE id = ?", (project_id,)
    )
    if not rows:
        raise HTTPException(404, detail="Project not found")
    return row_to_dict(rows[0])


@router.get("/api/projects/{project_id}/pruning-config", response_model=ContextPruningConfig)
async def get_pruning_config(project_id: str, db=Depends(get_db)):
    rows = await db.execute_fetchall(
        "SELECT context_pruning FROM projects WHERE id = ?", (project_id,)
    )
    if not rows:
        raise HTTPException(404, detail="Project not found")
    raw = rows[0]["context_pruning"]
    if not raw:
        return ContextPruningConfig()
    return ContextPruningConfig(**json.loads(raw))


@router.put("/api/projects/{project_id}/pruning-config", response_model=ContextPruningConfig)
async def update_pruning_config(
    project_id: str, body: ContextPruningConfig, db=Depends(get_db)
):
    rows = await db.execute_fetchall(
        "SELECT id FROM projects WHERE id = ?", (project_id,)
    )
    if not rows:
        raise HTTPException(404, detail="Project not found")
    await db.execute(
        "UPDATE projects SET context_pruning = ? WHERE id = ?",
        (json.dumps(body.model_dump()), project_id),
    )
    await db.commit()
    return body
