from typing import List, Optional
from uuid import UUID

from fastapi import APIRouter, Depends, Query, Request
from fastapi.responses import StreamingResponse
from sqlalchemy.ext.asyncio import AsyncSession

from src.apps.records.schemas.requests import (
    CreateBurialInfoRequest,
    CreateFamilyContactRequest,
    CreateRecordRequest,
    UpdateFamilyContactRequest,
    UpdateRecordRequest,
)
from src.apps.records.schemas.responses import (
    BurialInfoResponse,
    FamilyContactResponse,
    RecordResponse,
    RecordSummaryResponse,
)
from src.apps.records.services.record_service import RecordService
from src.core.constants import UserRole
from src.core.dependencies import require_min_role, require_roles
from src.core.schemas.response import paginated, success
from src.database.session import get_db

router = APIRouter(prefix="/records", tags=["Records"])


def serialize_record_for_list(r) -> dict:
    base = RecordSummaryResponse.model_validate(r).model_dump()
    base['plot_ref'] = r.plot.plot_ref if r.plot else None
    base['section_code'] = r.plot.section.code if r.plot and r.plot.section else None
    base['date_interred'] = (
        r.burial_info.interment_date.isoformat()
        if r.burial_info and r.burial_info.interment_date
        else None
    )
    return base


@router.get("", response_model=dict)
async def list_records(
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    search: Optional[str] = Query(None),
    section: Optional[str] = Query(None),
    record_type: Optional[str] = Query(None),
    is_veteran: Optional[bool] = Query(None),
    current_user=Depends(require_min_role(UserRole.VIEW_ONLY)),
    db: AsyncSession = Depends(get_db),
):
    service = RecordService(db)
    records, total = await service.list_records(
        tenant_id=current_user.tenant_id,
        search=search,
        section=section,
        record_type=record_type,
        page=page,
        page_size=page_size,
    )
    return paginated(
        items=[serialize_record_for_list(r) for r in records],
        total=total,
        page=page,
        page_size=page_size,
    )


@router.post("", response_model=dict, status_code=201)
async def create_record(
    body: CreateRecordRequest,
    request: Request,
    current_user=Depends(
        require_roles(UserRole.ADMINISTRATOR, UserRole.MANAGER, UserRole.STAFF)
    ),
    db: AsyncSession = Depends(get_db),
):
    service = RecordService(db)
    record = await service.create(
        tenant_id=current_user.tenant_id,
        data=body.model_dump(exclude_none=True),
        current_user=current_user,
        request=request,
    )
    return success(
        data=RecordResponse.model_validate(record).model_dump(),
        message="Record created",
    )


@router.get("/export-csv")
async def export_records_csv(
    search: Optional[str] = Query(None),
    section: Optional[str] = Query(None),
    record_type: Optional[str] = Query(None),
    record_ids: Optional[List[UUID]] = Query(None),
    current_user=Depends(require_min_role(UserRole.VIEW_ONLY)),
    db: AsyncSession = Depends(get_db),
):
    service = RecordService(db)
    generator = service.export_csv(
        tenant_id=current_user.tenant_id,
        record_ids=record_ids,
        search=search,
        section=section,
        record_type=record_type,
    )
    return StreamingResponse(
        generator,
        media_type="text/csv",
        headers={"Content-Disposition": "attachment; filename=records.csv"},
    )


@router.get("/{record_id}", response_model=dict)
async def get_record(
    record_id: UUID,
    current_user=Depends(require_min_role(UserRole.VIEW_ONLY)),
    db: AsyncSession = Depends(get_db),
):
    service = RecordService(db)
    record = await service.get_by_id(record_id, current_user.tenant_id)
    data = RecordResponse.model_validate(record).model_dump()
    data["plot_ref"] = record.plot.plot_ref if record.plot else None
    data["section_code"] = record.plot.section.code if record.plot and record.plot.section else None
    data["date_interred"] = (
        record.burial_info.interment_date.isoformat()
        if record.burial_info and record.burial_info.interment_date
        else None
    )
    return success(data=data)


@router.put("/{record_id}", response_model=dict)
async def update_record(
    record_id: UUID,
    body: UpdateRecordRequest,
    request: Request,
    current_user=Depends(
        require_roles(UserRole.ADMINISTRATOR, UserRole.MANAGER, UserRole.STAFF)
    ),
    db: AsyncSession = Depends(get_db),
):
    service = RecordService(db)
    record = await service.update(
        record_id=record_id,
        tenant_id=current_user.tenant_id,
        data=body.model_dump(exclude_none=True),
        current_user=current_user,
        request=request,
    )
    data = RecordResponse.model_validate(record).model_dump()
    data["plot_ref"] = record.plot.plot_ref if record.plot else None
    data["section_code"] = record.plot.section.code if record.plot and record.plot.section else None
    data["date_interred"] = (
        record.burial_info.interment_date.isoformat()
        if record.burial_info and record.burial_info.interment_date
        else None
    )
    return success(
        data=data,
        message="Record updated",
    )


@router.delete("/{record_id}", status_code=204)
async def delete_record(
    record_id: UUID,
    request: Request,
    current_user=Depends(require_roles(UserRole.ADMINISTRATOR)),
    db: AsyncSession = Depends(get_db),
):
    service = RecordService(db)
    await service.soft_delete(
        record_id,
        current_user.tenant_id,
        current_user=current_user,
        request=request,
    )


# --- Burial Info sub-resource ---

@router.put("/{record_id}/burial-info", response_model=dict)
async def upsert_burial_info(
    record_id: UUID,
    body: CreateBurialInfoRequest,
    current_user=Depends(
        require_roles(UserRole.ADMINISTRATOR, UserRole.MANAGER, UserRole.STAFF)
    ),
    db: AsyncSession = Depends(get_db),
):
    service = RecordService(db)
    burial = await service.upsert_burial_info(
        record_id=record_id,
        tenant_id=current_user.tenant_id,
        data=body.model_dump(exclude_none=True),
    )
    return success(
        data=BurialInfoResponse.model_validate(burial).model_dump(),
        message="Burial info saved",
    )


# --- Family Contacts sub-resource ---

@router.post("/{record_id}/contacts", response_model=dict, status_code=201)
async def add_family_contact(
    record_id: UUID,
    body: CreateFamilyContactRequest,
    current_user=Depends(
        require_roles(UserRole.ADMINISTRATOR, UserRole.MANAGER, UserRole.STAFF)
    ),
    db: AsyncSession = Depends(get_db),
):
    service = RecordService(db)
    contact = await service.add_family_contact(
        record_id=record_id,
        tenant_id=current_user.tenant_id,
        data=body.model_dump(exclude_none=True),
    )
    return success(
        data=FamilyContactResponse.model_validate(contact).model_dump(),
        message="Contact added",
    )


@router.delete("/{record_id}/contacts/{contact_id}", status_code=204)
async def delete_family_contact(
    record_id: UUID,
    contact_id: UUID,
    current_user=Depends(
        require_roles(UserRole.ADMINISTRATOR, UserRole.MANAGER, UserRole.STAFF)
    ),
    db: AsyncSession = Depends(get_db),
):
    service = RecordService(db)
    await service.delete_family_contact(contact_id, current_user.tenant_id)


@router.patch("/{record_id}/contacts/{contact_id}", response_model=dict)
async def update_family_contact(
    record_id: UUID,
    contact_id: UUID,
    body: UpdateFamilyContactRequest,
    current_user=Depends(
        require_roles(UserRole.ADMINISTRATOR, UserRole.MANAGER, UserRole.STAFF)
    ),
    db: AsyncSession = Depends(get_db),
):
    service = RecordService(db)
    contact = await service.update_family_contact(
        contact_id=contact_id,
        tenant_id=current_user.tenant_id,
        record_id=record_id,
        data=body.model_dump(exclude_none=True),
    )
    return success(
        data=FamilyContactResponse.model_validate(contact).model_dump(),
        message="Contact updated",
    )
