from uuid import UUID

from fastapi import APIRouter, Depends, Query, Response, status
from sqlalchemy.ext.asyncio import AsyncSession

from src.apps.sections.schemas.requests import (
    AutogenerateGridRequest,
    CreateSectionRequest,
    UpdateSectionRequest,
)
from src.apps.sections.schemas.responses import (
    AutogenerateGridResponse,
    serialize_section,
)
from src.apps.sections.services.section_service import SectionService
from src.core.constants import UserRole
from src.core.dependencies import require_roles
from src.core.exceptions import NotFoundError
from src.core.schemas.response import paginated, success
from src.database.session import get_db

router = APIRouter(prefix="/sections", tags=["Sections"])


@router.get("", response_model=dict)
async def list_sections(
    skip: int = Query(0, ge=0),
    limit: int = Query(50, ge=1, le=200),
    current_user=Depends(
        require_roles(
            UserRole.ADMINISTRATOR,
            UserRole.MANAGER,
            UserRole.STAFF,
            UserRole.VIEW_ONLY,
        )
    ),
    db: AsyncSession = Depends(get_db),
):
    """List all sections for the current tenant."""
    service = SectionService(db)
    sections, total = await service.list_sections(
        tenant_id=current_user.tenant_id,
        skip=skip,
        limit=limit,
    )
    counts = await service.status_counts(current_user.tenant_id)
    return paginated(
        items=[
            serialize_section(
                s,
                plot_count=counts.get(s.id, {}).get("total", 0),
                available_count=counts.get(s.id, {}).get("available", 0),
            )
            for s in sections
        ],
        total=total,
        page=(skip // limit) + 1 if limit else 1,
        page_size=limit,
    )


@router.post("", response_model=dict, status_code=201)
async def create_section(
    body: CreateSectionRequest,
    current_user=Depends(
        require_roles(UserRole.ADMINISTRATOR, UserRole.MANAGER)
    ),
    db: AsyncSession = Depends(get_db),
):
    """Create a new section (optionally with a drawn boundary)."""
    service = SectionService(db)
    section = await service.create(
        tenant_id=current_user.tenant_id,
        data=body.model_dump(exclude_unset=True),
    )
    return success(data=serialize_section(section), message="Section created")


@router.get("/{section_id}", response_model=dict)
async def get_section(
    section_id: UUID,
    current_user=Depends(
        require_roles(
            UserRole.ADMINISTRATOR,
            UserRole.MANAGER,
            UserRole.STAFF,
            UserRole.VIEW_ONLY,
        )
    ),
    db: AsyncSession = Depends(get_db),
):
    """Get a single section by ID."""
    service = SectionService(db)
    section = await service.get_by_id(section_id, current_user.tenant_id)
    if not section:
        raise NotFoundError("Section not found")
    return success(data=serialize_section(section))


@router.patch("/{section_id}", response_model=dict)
async def update_section(
    section_id: UUID,
    body: UpdateSectionRequest,
    current_user=Depends(
        require_roles(UserRole.ADMINISTRATOR, UserRole.MANAGER)
    ),
    db: AsyncSession = Depends(get_db),
):
    """Update a section — including drawing/clearing its boundary (boundary: null)."""
    service = SectionService(db)
    section = await service.update(
        section_id=section_id,
        tenant_id=current_user.tenant_id,
        # exclude_unset preserves PATCH semantics: an explicit `boundary: null`
        # clears the boundary, while an omitted field is left untouched.
        data=body.model_dump(exclude_unset=True),
    )
    return success(data=serialize_section(section), message="Section updated")


@router.delete("/{section_id}", status_code=204)
async def delete_section(
    section_id: UUID,
    current_user=Depends(require_roles(UserRole.ADMINISTRATOR)),
    db: AsyncSession = Depends(get_db),
):
    """Delete a section (Administrator only)."""
    service = SectionService(db)
    await service.delete(section_id, current_user.tenant_id)


@router.post("/{section_id}/plots/autogenerate", response_model=dict, status_code=201)
async def autogenerate_plots(
    section_id: UUID,
    body: AutogenerateGridRequest,
    response: Response,
    current_user=Depends(
        require_roles(UserRole.ADMINISTRATOR, UserRole.MANAGER)
    ),
    db: AsyncSession = Depends(get_db),
):
    """Tile a rectangle within the section into a numbered grid of plots (INDL-49).

    With ``preview=true`` (INDL-55 E-07) nothing is created — the response reports
    how many plots fit in the section at the requested plot/spacing size so the UI
    can warn before the admin confirms.
    """
    service = SectionService(db)
    result = await service.autogenerate_plots(
        section_id=section_id,
        tenant_id=current_user.tenant_id,
        data=body.model_dump(),
    )
    if result.get("preview"):
        response.status_code = status.HTTP_200_OK  # preview creates nothing
        return success(
            data=result,
            message=(
                f"Section fits at most {result['max_plots_fit']} plots at this size"
                if result.get("max_plots_fit") is not None
                else "Draw the section boundary to see its plot capacity"
            ),
        )
    return success(
        data=AutogenerateGridResponse(**result).model_dump(),
        message=f"{result['created']} plots created",
    )
