from typing import Optional
from uuid import UUID

from fastapi import APIRouter, Depends, File, Form, Query, Request, UploadFile
from sqlalchemy.ext.asyncio import AsyncSession

from src.apps.documents.schemas.requests import ALLOWED_CATEGORIES, DocumentUploadUrlRequest
from src.apps.documents.schemas.responses import DocumentResponse
from src.apps.documents.services.document_service import ALLOWED_MIME_TYPES, DocumentService
from src.apps.records.services.record_service import RecordService
from src.core.constants import UserRole
from src.core.dependencies import require_min_role
from src.core.exceptions import ValidationError
from src.core.schemas.response import paginated, success
from src.database.session import get_db

router = APIRouter(prefix="/records/{record_id}/documents", tags=["Documents"])


@router.post("", response_model=dict, status_code=201)
async def upload_document(
    record_id: UUID,
    request: Request,
    file: UploadFile = File(...),
    category: Optional[str] = Form(None),
    current_user=Depends(require_min_role(UserRole.STAFF)),
    db: AsyncSession = Depends(get_db),
):
    """Proxy upload: browser sends file to API, API uploads to S3 server-side.

    Avoids the need for S3 bucket CORS configuration.
    """
    if category is not None and category not in ALLOWED_CATEGORIES:
        raise ValidationError(
            message="category must be one of: death_certificate, burial_permit, contract, other"
        )

    mime_type = file.content_type or "application/octet-stream"
    if mime_type not in ALLOWED_MIME_TYPES:
        raise ValidationError(
            message=f"Unsupported file type. Allowed: PDF, JPEG, PNG, TIFF."
        )

    record_service = RecordService(db)
    await record_service.get_by_id(record_id, current_user.tenant_id)

    content = await file.read()

    service = DocumentService(db)
    doc = await service.upload_file(
        tenant_id=current_user.tenant_id,
        entity_type="record",
        entity_id=record_id,
        filename=file.filename or "upload",
        mime_type=mime_type,
        file_content=content,
        file_size_bytes=len(content),
        category=category,
        uploaded_by=current_user.id,
        current_user=current_user,
        request=request,
    )
    return success(
        data=DocumentResponse.from_orm_doc(doc).model_dump(),
        message="Document uploaded",
    )


@router.post("/upload-url", response_model=dict, status_code=201)
async def get_upload_url(
    record_id: UUID,
    body: DocumentUploadUrlRequest,
    request: Request,
    current_user=Depends(require_min_role(UserRole.STAFF)),
    db: AsyncSession = Depends(get_db),
):
    # F-05: verify record belongs to this tenant before proceeding
    record_service = RecordService(db)
    await record_service.get_by_id(record_id, current_user.tenant_id)

    service = DocumentService(db)
    result = await service.get_upload_url(
        tenant_id=current_user.tenant_id,
        entity_type="record",
        entity_id=record_id,
        filename=body.filename,
        mime_type=body.mime_type,
        file_size_bytes=body.file_size_bytes,
        category=body.category,
        uploaded_by=current_user.id,
    )
    return success(data=result, message="Upload URL generated")


@router.post("/{doc_id}/confirm", response_model=dict)
async def confirm_upload(
    record_id: UUID,
    doc_id: UUID,
    request: Request,
    file_size_bytes: Optional[int] = Query(None, gt=0),
    current_user=Depends(require_min_role(UserRole.STAFF)),
    db: AsyncSession = Depends(get_db),
):
    # F-05: verify record belongs to this tenant before proceeding
    record_service = RecordService(db)
    await record_service.get_by_id(record_id, current_user.tenant_id)

    service = DocumentService(db)
    doc = await service.confirm_upload(
        tenant_id=current_user.tenant_id,
        document_id=doc_id,
        uploaded_by=current_user.id,   # F-03: bind confirm to originating user
        current_user=current_user,
        request=request,
        file_size_bytes=file_size_bytes,
    )
    return success(
        data=DocumentResponse.from_orm_doc(doc).model_dump(),
        message="Upload confirmed",
    )


@router.get("", response_model=dict)
async def list_documents(
    record_id: UUID,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    current_user=Depends(require_min_role(UserRole.VIEW_ONLY)),
    db: AsyncSession = Depends(get_db),
):
    # F-05: verify record belongs to this tenant before proceeding
    record_service = RecordService(db)
    await record_service.get_by_id(record_id, current_user.tenant_id)

    service = DocumentService(db)
    docs, total = await service.get_list(
        tenant_id=current_user.tenant_id,
        entity_type="record",
        entity_id=record_id,
        page=page,
        page_size=page_size,
    )
    return paginated(
        items=[DocumentResponse.from_orm_doc(d).model_dump() for d in docs],
        total=total,
        page=page,
        page_size=page_size,
    )


@router.get("/{doc_id}/download", response_model=dict)
async def get_download_url(
    record_id: UUID,
    doc_id: UUID,
    request: Request,
    current_user=Depends(require_min_role(UserRole.VIEW_ONLY)),
    db: AsyncSession = Depends(get_db),
):
    # F-05: verify record belongs to this tenant before proceeding
    record_service = RecordService(db)
    await record_service.get_by_id(record_id, current_user.tenant_id)

    service = DocumentService(db)
    url = await service.get_download_url(
        tenant_id=current_user.tenant_id,
        document_id=doc_id,
        current_user=current_user,
        request=request,
    )
    return success(data={"download_url": url})


@router.delete("/{doc_id}", status_code=204)
async def delete_document(
    record_id: UUID,
    doc_id: UUID,
    request: Request,
    current_user=Depends(require_min_role(UserRole.MANAGER)),
    db: AsyncSession = Depends(get_db),
):
    # F-05: verify record belongs to this tenant before proceeding
    record_service = RecordService(db)
    await record_service.get_by_id(record_id, current_user.tenant_id)

    service = DocumentService(db)
    await service.soft_delete(
        tenant_id=current_user.tenant_id,
        document_id=doc_id,
        current_user=current_user,
        request=request,
    )
