"""Project REST API routes."""

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

import asyncpg
from fastapi import APIRouter, Depends, HTTPException, Query, Request

from peon_mcp.common.auth import AuthInfo, get_auth
from peon_mcp.common.dependencies import get_db, get_project_id, get_related_project_id
from peon_mcp.common.schemas import PaginatedResponse
from peon_mcp.common.scoping import (
    authorize_project_access,
    get_accessible_project_ids,
    _get_user_org_logins,
)
from peon_mcp.db import row_to_dict
from peon_mcp.projects.schemas import (
    ActivityDataPoint,
    AddProjectRelationRequest,
    CreateProjectRequest,
    DeployConfig,
    K8sConfig,
    ProjectActivityResponse,
    ProjectRelationResponse,
    ProjectResponse,
    ProjectStatsResponse,
    UpdateProjectRequest,
)

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


async def _enrich_owner_login(db, project: dict) -> dict:
    """Add owner_login to a project dict if owner_id is set."""
    owner_id = project.get("owner_id")
    if owner_id:
        rows = await db.execute_fetchall(
            "SELECT login FROM users WHERE id = ?", (owner_id,)
        )
        if rows:
            project["owner_login"] = rows[0]["login"]
    return project


@router.get("/api/projects", response_model=list[ProjectResponse])
async def list_projects(
    request: Request,
    auth: AuthInfo = Depends(get_auth),
    db=Depends(get_db),
):
    if auth.is_session:
        # Scoped query: owned + org-visible
        user_id = auth.user_id
        org_logins = await _get_user_org_logins(db, auth, request)

        conditions = ["p.owner_id = ?"]
        params: list = [user_id]

        for org in org_logins:
            conditions.append("p.github_url ILIKE ?")
            params.append(f"%github.com/{org}/%")

        where = " OR ".join(conditions)
        rows = await db.execute_fetchall(
            f"SELECT p.*, u.login AS owner_login FROM projects p"
            f" LEFT JOIN users u ON p.owner_id = u.id"
            f" WHERE {where}"
            f" ORDER BY p.created_at DESC",
            params,
        )
    else:
        # API key / no auth → all projects
        rows = await db.execute_fetchall(
            "SELECT p.*, u.login AS owner_login FROM projects p"
            " LEFT JOIN users u ON p.owner_id = u.id"
            " ORDER BY p.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,
    auth: AuthInfo = Depends(get_auth),
    db=Depends(get_db),
):
    project_id = body.id.strip()
    if not project_id:
        raise HTTPException(400, detail="id is required")
    if "/" not in project_id or project_id.count("/") != 1:
        raise HTTPException(400, detail="Project ID must be in 'owner/repo' format (e.g. 'pyrabbit/peon-mcp')")
    path = body.path.strip()

    # Set owner_id from session auth (NULL for API key)
    owner_id = auth.user_id if auth.is_session else None

    try:
        await db.execute(
            "INSERT INTO projects (id, path, github_url, capture_hostname, owner_id) VALUES (?, ?, ?, ?, ?)",
            (project_id, path, body.github_url.strip(), body.capture_hostname.strip(), owner_id),
        )
        await db.commit()
        rows = await db.execute_fetchall(
            "SELECT p.*, u.login AS owner_login FROM projects p"
            " LEFT JOIN users u ON p.owner_id = u.id"
            " WHERE p.id = ?",
            (project_id,),
        )
        return row_to_dict(rows[0])
    except asyncpg.UniqueViolationError:
        raise HTTPException(409, detail=f"Project '{project_id}' already exists")


@router.put("/api/projects/{owner}/{repo}", response_model=ProjectResponse)
async def update_project(
    body: UpdateProjectRequest,
    request: Request,
    project_id: str = Depends(get_project_id),
    auth: AuthInfo = Depends(get_auth),
    db=Depends(get_db),
):
    await authorize_project_access(db, auth, project_id, request)

    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 body.capture_hostname is not None:
        fields.append("capture_hostname = ?")
        params.append(body.capture_hostname.strip())
    if not fields:
        rows = await db.execute_fetchall(
            "SELECT p.*, u.login AS owner_login FROM projects p"
            " LEFT JOIN users u ON p.owner_id = u.id"
            " WHERE p.id = ?",
            (project_id,),
        )
        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 p.*, u.login AS owner_login FROM projects p"
        " LEFT JOIN users u ON p.owner_id = u.id"
        " WHERE p.id = ?",
        (project_id,),
    )
    return row_to_dict(rows[0])


@router.get("/api/projects/{owner}/{repo}/stats", response_model=ProjectStatsResponse)
async def project_stats(
    request: Request,
    project_id: str = Depends(get_project_id),
    auth: AuthInfo = Depends(get_auth),
    db=Depends(get_db),
):
    await authorize_project_access(db, auth, project_id, request)

    # 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"]

    # 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(EXTRACT(EPOCH FROM (completed_at - started_at))) as avg_duration,
            MIN(EXTRACT(EPOCH FROM (completed_at - started_at))) as min_duration,
            MAX(EXTRACT(EPOCH FROM (completed_at - started_at))) 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,
    }

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

    return response_data


@router.get("/api/projects/{owner}/{repo}/activity", response_model=ProjectActivityResponse)
async def project_activity(
    request: Request,
    hours: int = Query(default=8, ge=1, le=720),
    project_id: str = Depends(get_project_id),
    auth: AuthInfo = Depends(get_auth),
    db=Depends(get_db),
):
    await authorize_project_access(db, auth, project_id, request)

    # Get task completions grouped by hour for the last N hours
    rows = await db.execute_fetchall(
        """
        SELECT
            TO_CHAR(DATE_TRUNC('hour', completed_at), 'YYYY-MM-DD HH24:00:00') as hour,
            COUNT(*) as count
        FROM tasks
        WHERE project_id = ?
        AND completed_at IS NOT NULL
        AND completed_at >= NOW() - make_interval(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/{owner}/{repo}", response_model=ProjectResponse)
async def get_project(
    request: Request,
    project_id: str = Depends(get_project_id),
    auth: AuthInfo = Depends(get_auth),
    db=Depends(get_db),
):
    await authorize_project_access(db, auth, project_id, request)
    rows = await db.execute_fetchall(
        "SELECT p.*, u.login AS owner_login FROM projects p"
        " LEFT JOIN users u ON p.owner_id = u.id"
        " WHERE p.id = ?",
        (project_id,),
    )
    return row_to_dict(rows[0])


@router.get("/api/projects/{owner}/{repo}/k8s-config", response_model=K8sConfig)
async def get_k8s_config(
    request: Request,
    project_id: str = Depends(get_project_id),
    auth: AuthInfo = Depends(get_auth),
    db=Depends(get_db),
):
    project = await authorize_project_access(db, auth, project_id, request)
    raw = project.get("k8s_config")
    if not raw:
        return K8sConfig()
    return K8sConfig(**json.loads(raw))


@router.put("/api/projects/{owner}/{repo}/k8s-config", response_model=K8sConfig)
async def update_k8s_config(
    body: K8sConfig,
    request: Request,
    project_id: str = Depends(get_project_id),
    auth: AuthInfo = Depends(get_auth),
    db=Depends(get_db),
):
    await authorize_project_access(db, auth, project_id, request)
    await db.execute(
        "UPDATE projects SET k8s_config = ? WHERE id = ?",
        (json.dumps(body.model_dump()), project_id),
    )
    await db.commit()
    return body


@router.get("/api/projects/{owner}/{repo}/deploy-config", response_model=DeployConfig)
async def get_deploy_config(
    request: Request,
    project_id: str = Depends(get_project_id),
    auth: AuthInfo = Depends(get_auth),
    db=Depends(get_db),
):
    project = await authorize_project_access(db, auth, project_id, request)
    raw = project.get("deploy_config")
    if not raw:
        return DeployConfig()
    return DeployConfig(**json.loads(raw))


@router.put("/api/projects/{owner}/{repo}/deploy-config", response_model=DeployConfig)
async def update_deploy_config(
    body: DeployConfig,
    request: Request,
    project_id: str = Depends(get_project_id),
    auth: AuthInfo = Depends(get_auth),
    db=Depends(get_db),
):
    await authorize_project_access(db, auth, project_id, request)
    await db.execute(
        "UPDATE projects SET deploy_config = ? WHERE id = ?",
        (json.dumps(body.model_dump()), project_id),
    )
    await db.commit()
    return body


# ---------------------------------------------------------------------------
# Project relation endpoints
# ---------------------------------------------------------------------------


@router.get(
    "/api/projects/{owner}/{repo}/relations",
    response_model=list[ProjectRelationResponse],
)
async def list_project_relations(
    request: Request,
    project_id: str = Depends(get_project_id),
    auth: AuthInfo = Depends(get_auth),
    db=Depends(get_db),
):
    """List all projects related to this project."""
    await authorize_project_access(db, auth, project_id, request)
    rel_rows = await db.execute_fetchall(
        "SELECT * FROM project_relations WHERE project_id = ? ORDER BY created_at ASC",
        (project_id,),
    )
    return [row_to_dict(r) for r in rel_rows]


@router.post(
    "/api/projects/{owner}/{repo}/relations",
    response_model=ProjectRelationResponse,
    status_code=201,
)
async def add_project_relation(
    body: AddProjectRelationRequest,
    request: Request,
    project_id: str = Depends(get_project_id),
    auth: AuthInfo = Depends(get_auth),
    db=Depends(get_db),
):
    """Add a relation between this project and another project."""
    await authorize_project_access(db, auth, project_id, request)

    related_id = body.related_project_id.strip()

    if project_id == related_id:
        raise HTTPException(400, detail="A project cannot be related to itself.")

    # Related project must also exist
    rows = await db.execute_fetchall(
        "SELECT id FROM projects WHERE id = ?", (related_id,)
    )
    if not rows:
        raise HTTPException(404, detail=f"Project '{related_id}' not found.")

    try:
        cursor = await db.execute(
            "INSERT INTO project_relations (project_id, related_project_id) VALUES (?, ?) RETURNING id",
            (project_id, related_id),
        )
        await db.commit()
    except Exception as exc:
        if isinstance(exc, asyncpg.UniqueViolationError):
            raise HTTPException(
                409,
                detail=f"Relation already exists: '{project_id}' is already related to '{related_id}'.",
            )
        raise HTTPException(500, detail=f"Failed to add relation: {exc}")

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


@router.delete(
    "/api/projects/{owner}/{repo}/relations/{related_owner}/{related_repo}",
    response_model=dict,
)
async def remove_project_relation(
    request: Request,
    project_id: str = Depends(get_project_id),
    related_project_id: str = Depends(get_related_project_id),
    auth: AuthInfo = Depends(get_auth),
    db=Depends(get_db),
):
    """Remove a relation between this project and another project."""
    await authorize_project_access(db, auth, project_id, request)

    cursor = await db.execute(
        "DELETE FROM project_relations WHERE project_id = ? AND related_project_id = ?",
        (project_id, related_project_id),
    )
    await db.commit()
    if cursor.rowcount == 0:
        raise HTTPException(
            404,
            detail=f"Relation not found: '{project_id}' is not related to '{related_project_id}'.",
        )
    return {"ok": True}
