"""Materials intake and the briefing export. Admin-only end to end (D14):
clients see exactly one thing — the review experience — never the raw inputs."""

from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile, status
from fastapi.responses import FileResponse, Response
from sqlalchemy.orm import Session

from app.constants import MAX_UPLOAD_BYTES, MaterialKind
from app.db import get_db
from app.models.account import Account
from app.models.schemas import LinkCreate, MaterialOut, NoteCreate
from app.services import briefing
from app.services import materials as materials_service
from app.services import storage
from app.services import levels
from app.services.authz import current_account, project_with_permission

router = APIRouter(prefix="/api/projects/{project_id}", tags=["materials"])


@router.get("/materials", response_model=list[MaterialOut])
def list_materials(
    project_id: int,
    admin: Account = Depends(current_account),
    db: Session = Depends(get_db),
) -> list[MaterialOut]:
    project = project_with_permission(db, project_id, admin, levels.PROJECT_MANAGE)
    return [
        MaterialOut.model_validate(material)
        for material in materials_service.list_for_project(db, project.id)
    ]


@router.post("/materials/note", response_model=MaterialOut, status_code=status.HTTP_201_CREATED)
def add_note(
    project_id: int,
    payload: NoteCreate,
    admin: Account = Depends(current_account),
    db: Session = Depends(get_db),
) -> MaterialOut:
    project = project_with_permission(db, project_id, admin, levels.PROJECT_MANAGE)
    material = materials_service.add_note(
        db, project.id, title=payload.title, body=payload.body, added_by=admin.username
    )
    return MaterialOut.model_validate(material)


@router.post("/materials/link", response_model=MaterialOut, status_code=status.HTTP_201_CREATED)
def add_link(
    project_id: int,
    payload: LinkCreate,
    admin: Account = Depends(current_account),
    db: Session = Depends(get_db),
) -> MaterialOut:
    project = project_with_permission(db, project_id, admin, levels.PROJECT_MANAGE)
    material = materials_service.add_link(
        db, project.id, title=payload.title, url=payload.url, added_by=admin.username
    )
    return MaterialOut.model_validate(material)


@router.post("/materials/file", response_model=MaterialOut, status_code=status.HTTP_201_CREATED)
def add_file(
    project_id: int,
    upload: UploadFile = File(...),
    title: str = Form(default=""),
    admin: Account = Depends(current_account),
    db: Session = Depends(get_db),
) -> MaterialOut:
    project = project_with_permission(db, project_id, admin, levels.PROJECT_MANAGE)
    content = upload.file.read(MAX_UPLOAD_BYTES + 1)
    try:
        material = materials_service.add_file(
            db,
            project.id,
            title=title,
            filename=upload.filename or "upload",
            content=content,
            mime=upload.content_type or "",
            added_by=admin.username,
        )
    except storage.StorageError as exc:
        raise HTTPException(
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
            detail={"error_code": exc.code, "summary": exc.summary, "details": exc.details},
        ) from exc
    return MaterialOut.model_validate(material)


@router.delete("/materials/{material_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_material(
    project_id: int,
    material_id: int,
    admin: Account = Depends(current_account),
    db: Session = Depends(get_db),
) -> None:
    project = project_with_permission(db, project_id, admin, levels.PROJECT_MANAGE)
    material = materials_service.get(db, project.id, material_id)
    if material is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail={"error_code": "MATERIAL_NOT_FOUND", "summary": "Material not found."},
        )
    materials_service.delete(db, material)


@router.get("/materials/{material_id}/file", response_class=FileResponse)
def material_file(
    project_id: int,
    material_id: int,
    admin: Account = Depends(current_account),
    db: Session = Depends(get_db),
):
    """Serve an uploaded material back to an admin — authed route, never /assets."""
    project = project_with_permission(db, project_id, admin, levels.PROJECT_MANAGE)
    material = materials_service.get(db, project.id, material_id)
    not_found = HTTPException(
        status_code=status.HTTP_404_NOT_FOUND,
        detail={"error_code": "MATERIAL_NOT_FOUND", "summary": "Material not found."},
    )
    if material is None or material.kind != MaterialKind.FILE or not material.file_path:
        raise not_found
    try:
        absolute = storage.resolve(material.file_path)
    except storage.StorageError as exc:
        raise not_found from exc
    if not absolute.is_file():
        raise not_found
    return FileResponse(
        absolute,
        media_type=material.mime or "application/octet-stream",
        filename=material.file_name or None,
    )


@router.get("/export")
def briefing_export(
    project_id: int,
    admin: Account = Depends(current_account),
    db: Session = Depends(get_db),
) -> Response:
    """Download the briefing package — everything a fresh Claude session needs
    to run the research step, contract included."""
    project = project_with_permission(db, project_id, admin, levels.PROJECT_MANAGE)
    payload = briefing.build_briefing_zip(db, project)
    filename = f"briefing-{project.slug}.zip"
    return Response(
        payload,
        media_type="application/zip",
        headers={"Content-Disposition": f'attachment; filename="{filename}"'},
    )
