"""Memory REST API routes."""

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

from peon_mcp.common.auth import AuthInfo, get_auth
from peon_mcp.common.constants import MAX_MEMORIES_PER_PROJECT, VALID_MEMORY_CATEGORIES
from peon_mcp.common.dependencies import get_db, get_project_id
from peon_mcp.common.schemas import PaginatedResponse
from peon_mcp.common.scoping import authorize_project_access, authorize_entity_project_access
from peon_mcp.db import row_to_dict
from peon_mcp.memories.schemas import CreateMemoryRequest, MemoryResponse, RecallMemoriesRequest

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


@router.get("/api/projects/{owner}/{repo}/memories", response_model=PaginatedResponse[MemoryResponse])
async def list_memories(
    request: Request,
    category: str | None = Query(default=None),
    limit: int = Query(default=50, ge=1, le=200),
    offset: int = Query(default=0, ge=0),
    project_id: str = Depends(get_project_id),
    auth: AuthInfo = Depends(get_auth),
    db=Depends(get_db),
):
    """List memories for a project (read-only, no side effects)."""
    await authorize_project_access(db, auth, project_id, request)

    params: list = [project_id]
    where = "WHERE project_id = ?"
    if category is not None:
        if category not in VALID_MEMORY_CATEGORIES:
            raise HTTPException(400, detail=f"Invalid category: {category}")
        where += " AND category = ?"
        params.append(category)

    count_rows = await db.execute_fetchall(
        f"SELECT COUNT(*) FROM peon_memories {where}", params,
    )
    total = count_rows[0]["count"] if count_rows else 0

    rows = await db.execute_fetchall(
        f"SELECT * FROM peon_memories {where} ORDER BY updated_at DESC LIMIT ? OFFSET ?",
        params + [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.post("/api/projects/{owner}/{repo}/memories", response_model=MemoryResponse, status_code=201)
async def create_memory(
    body: CreateMemoryRequest,
    request: Request,
    project_id: str = Depends(get_project_id),
    auth: AuthInfo = Depends(get_auth),
    db=Depends(get_db),
):
    """Create a memory entry, auto-evicting the least-used if at capacity."""
    await authorize_project_access(db, auth, project_id, request)

    content = body.content.strip()
    if not content:
        raise HTTPException(400, detail="content is required")
    if len(content) > 500:
        raise HTTPException(400, detail="content must be 500 characters or fewer")
    if body.category not in VALID_MEMORY_CATEGORIES:
        raise HTTPException(400, detail=f"Invalid category: {body.category}")

    # Verify project exists
    proj_rows = await db.execute_fetchall(
        "SELECT id FROM projects WHERE id = ?", (project_id,),
    )
    if not proj_rows:
        raise HTTPException(404, detail=f"Project '{project_id}' not found")

    # Evict if at capacity
    count_rows = await db.execute_fetchall(
        "SELECT COUNT(*) FROM peon_memories WHERE project_id = ?", (project_id,),
    )
    current_count = count_rows[0]["count"] if count_rows else 0
    if current_count >= MAX_MEMORIES_PER_PROJECT:
        await db.execute(
            "DELETE FROM peon_memories WHERE id = ("
            "  SELECT id FROM peon_memories WHERE project_id = ?"
            "  ORDER BY access_count ASC, created_at ASC LIMIT 1"
            ")",
            (project_id,),
        )

    cursor = await db.execute(
        "INSERT INTO peon_memories (project_id, category, content, source_task_id) VALUES (?, ?, ?, ?) RETURNING id",
        (project_id, body.category, content, body.source_task_id),
    )
    new_id = cursor.lastrowid
    await db.commit()
    rows = await db.execute_fetchall(
        "SELECT * FROM peon_memories WHERE id = ?", (new_id,),
    )
    return row_to_dict(rows[0])


@router.post("/api/projects/{owner}/{repo}/memories/recall", response_model=list[MemoryResponse])
async def recall_memories(
    body: RecallMemoriesRequest,
    request: Request,
    project_id: str = Depends(get_project_id),
    auth: AuthInfo = Depends(get_auth),
    db=Depends(get_db),
):
    """Recall memories and increment their access counts (POST due to side effect)."""
    await authorize_project_access(db, auth, project_id, request)

    params: list = [project_id]
    where = "WHERE project_id = ?"
    if body.category is not None:
        if body.category not in VALID_MEMORY_CATEGORIES:
            raise HTTPException(400, detail=f"Invalid category: {body.category}")
        where += " AND category = ?"
        params.append(body.category)

    rows = await db.execute_fetchall(
        f"SELECT * FROM peon_memories {where} ORDER BY access_count DESC, updated_at DESC LIMIT ?",
        params + [body.limit],
    )
    items = [row_to_dict(r) for r in rows]

    # Increment access_count and update timestamp for recalled memories
    if items:
        ids = [m["id"] for m in items]
        placeholders = ",".join("?" * len(ids))
        await db.execute(
            f"UPDATE peon_memories SET access_count = access_count + 1, updated_at = CURRENT_TIMESTAMP "
            f"WHERE id IN ({placeholders})",
            ids,
        )
        await db.commit()
        # Re-fetch to return updated counts
        rows = await db.execute_fetchall(
            f"SELECT * FROM peon_memories WHERE id IN ({placeholders}) "
            "ORDER BY access_count DESC, updated_at DESC",
            ids,
        )
        items = [row_to_dict(r) for r in rows]

    return items


@router.delete("/api/memories/{memory_id}", response_model=MemoryResponse)
async def delete_memory(
    memory_id: int,
    request: Request,
    auth: AuthInfo = Depends(get_auth),
    db=Depends(get_db),
):
    """Delete a memory entry."""
    await authorize_entity_project_access(
        db, auth, request, "peon_memories", memory_id,
    )

    rows = await db.execute_fetchall(
        "SELECT * FROM peon_memories WHERE id = ?", (memory_id,),
    )
    if not rows:
        raise HTTPException(404, detail=f"Memory {memory_id} not found")

    result = row_to_dict(rows[0])
    await db.execute("DELETE FROM peon_memories WHERE id = ?", (memory_id,))
    await db.commit()
    return result
