"""Project and Frame CRUD endpoints."""

import logging
from typing import List

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session

from database import get_db
from models import Project, Frame
from schemas import ProjectCreate, ProjectOut, ProjectUpdate, FrameCreate, FrameOut
from minio_client import get_presigned_url

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/projects", tags=["Projects"])


# ---------------------------------------------------------------------------
# Projects
# ---------------------------------------------------------------------------
@router.post(
    "",
    response_model=ProjectOut,
    status_code=status.HTTP_201_CREATED,
    summary="Create a new project",
)
def create_project(payload: ProjectCreate, db: Session = Depends(get_db)) -> Project:
    """Create a new segmentation project."""
    project = Project(**payload.model_dump())
    db.add(project)
    db.commit()
    db.refresh(project)
    logger.info("Created project id=%s name=%s", project.id, project.name)
    return project


@router.get(
    "",
    response_model=List[ProjectOut],
    summary="List all projects",
)
def list_projects(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)) -> List[Project]:
    """Retrieve a paginated list of projects."""
    projects = db.query(Project).offset(skip).limit(limit).all()
    for p in projects:
        p.frame_count = len(p.frames)
        if p.thumbnail_url:
            p.thumbnail_url = get_presigned_url(p.thumbnail_url, expires=3600)
    return projects


@router.get(
    "/{project_id}",
    response_model=ProjectOut,
    summary="Get a single project",
)
def get_project(project_id: int, db: Session = Depends(get_db)) -> Project:
    """Retrieve a project by its ID."""
    project = db.query(Project).filter(Project.id == project_id).first()
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")
    project.frame_count = len(project.frames)
    if project.thumbnail_url:
        project.thumbnail_url = get_presigned_url(project.thumbnail_url, expires=3600)
    return project


@router.patch(
    "/{project_id}",
    response_model=ProjectOut,
    summary="Update a project",
)
def update_project(
    project_id: int,
    payload: ProjectUpdate,
    db: Session = Depends(get_db),
) -> Project:
    """Update project fields partially."""
    project = db.query(Project).filter(Project.id == project_id).first()
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    for key, value in payload.model_dump(exclude_unset=True).items():
        setattr(project, key, value)

    db.commit()
    db.refresh(project)
    logger.info("Updated project id=%s", project_id)
    return project


@router.delete(
    "/{project_id}",
    status_code=status.HTTP_204_NO_CONTENT,
    summary="Delete a project",
)
def delete_project(project_id: int, db: Session = Depends(get_db)) -> None:
    """Delete a project and all related frames and annotations."""
    project = db.query(Project).filter(Project.id == project_id).first()
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    db.delete(project)
    db.commit()
    logger.info("Deleted project id=%s", project_id)


# ---------------------------------------------------------------------------
# Frames
# ---------------------------------------------------------------------------
@router.post(
    "/{project_id}/frames",
    response_model=FrameOut,
    status_code=status.HTTP_201_CREATED,
    summary="Add a frame to a project",
)
def create_frame(
    project_id: int,
    payload: FrameCreate,
    db: Session = Depends(get_db),
) -> Frame:
    """Register a new frame under a project."""
    project = db.query(Project).filter(Project.id == project_id).first()
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    frame = Frame(project_id=project_id, **payload.model_dump(exclude={"project_id"}))
    db.add(frame)
    db.commit()
    db.refresh(frame)
    return frame


@router.get(
    "/{project_id}/frames",
    response_model=List[FrameOut],
    summary="List frames for a project",
)
def list_frames(
    project_id: int,
    skip: int = 0,
    limit: int = 1000,
    db: Session = Depends(get_db),
) -> List[Frame]:
    """Retrieve all frames belonging to a project."""
    project = db.query(Project).filter(Project.id == project_id).first()
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    frames = (
        db.query(Frame)
        .filter(Frame.project_id == project_id)
        .order_by(Frame.frame_index)
        .offset(skip)
        .limit(limit)
        .all()
    )
    for frame in frames:
        frame.image_url = get_presigned_url(frame.image_url, expires=3600)
    return frames


@router.get(
    "/{project_id}/frames/{frame_id}",
    response_model=FrameOut,
    summary="Get a single frame",
)
def get_frame(project_id: int, frame_id: int, db: Session = Depends(get_db)) -> Frame:
    """Retrieve a specific frame by ID."""
    frame = (
        db.query(Frame)
        .filter(Frame.project_id == project_id, Frame.id == frame_id)
        .first()
    )
    if not frame:
        raise HTTPException(status_code=404, detail="Frame not found")
    return frame
