"""Appointment scheduling APIs — Phase 13.1."""
from __future__ import annotations

from datetime import datetime
from uuid import UUID

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

from app.api.deps import get_db, get_pagination, require_tenant
from app.api.permissions import require_permissions
from app.commands.appointments import AppointmentCommands
from app.models.types import AppointmentStatus
from app.permissions.definitions import APPOINTMENTS_MANAGE, APPOINTMENTS_VIEW, SCHEDULES_MANAGE, SCHEDULES_VIEW
from app.queries.appointments import AppointmentQueries
from app.schemas.appointments import (
    AppointmentCreate,
    AppointmentLifecycleRequest,
    AppointmentListResponse,
    AppointmentRead,
    AppointmentReschedule,
    AppointmentTypeCreate,
    AppointmentTypeRead,
    AvailabilityWindowCreate,
    AvailabilityWindowRead,
    ScheduleCreate,
    ScheduleRead,
    WaitingListEntryCreate,
    WaitingListEntryRead,
)
from app.specifications.appointments import ALLOWED_SORT_FIELDS, AppointmentListSpec
from shared.pagination import PaginationParams
from shared.security import CurrentUser

schedules_router = APIRouter()
availability_router = APIRouter()
appointment_types_router = APIRouter()
appointments_router = APIRouter()
waiting_list_router = APIRouter()


def _appointment_spec(
    status_filter: AppointmentStatus | None = Query(default=None, alias="status"),
    doctor_id: UUID | None = Query(default=None),
    clinic_id: UUID | None = Query(default=None),
    patient_id: UUID | None = Query(default=None),
    date_from: datetime | None = Query(default=None),
    date_to: datetime | None = Query(default=None),
    q: str | None = Query(default=None, max_length=100),
    sort_by: str = Query(default="scheduled_start"),
    sort_dir: str = Query(default="desc", pattern="^(?i)(asc|desc)$"),
) -> AppointmentListSpec:
    if sort_by not in ALLOWED_SORT_FIELDS:
        sort_by = "scheduled_start"
    return AppointmentListSpec(
        status=status_filter,
        doctor_id=doctor_id,
        clinic_id=clinic_id,
        patient_id=patient_id,
        date_from=date_from,
        date_to=date_to,
        q=q,
        sort_by=sort_by,
        sort_dir=sort_dir.lower(),
    )


@schedules_router.post("", response_model=ScheduleRead, status_code=status.HTTP_201_CREATED)
async def create_schedule(
    body: ScheduleCreate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    user: CurrentUser = Depends(require_permissions(SCHEDULES_MANAGE)),
):
    return await AppointmentCommands(db).create_schedule(tenant_id, body, actor=user)


@schedules_router.get("", response_model=list[ScheduleRead])
async def list_schedules(
    tenant_id: UUID = Depends(require_tenant),
    pagination: PaginationParams = Depends(get_pagination),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(require_permissions(SCHEDULES_VIEW)),
):
    return await AppointmentQueries(db).list_schedules(
        tenant_id, offset=pagination.offset, limit=pagination.page_size
    )


@availability_router.post("", response_model=AvailabilityWindowRead, status_code=status.HTTP_201_CREATED)
async def create_availability(
    body: AvailabilityWindowCreate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    user: CurrentUser = Depends(require_permissions(SCHEDULES_MANAGE)),
):
    return await AppointmentCommands(db).create_availability(tenant_id, body, actor=user)


@availability_router.get("", response_model=list[AvailabilityWindowRead])
async def list_availability(
    tenant_id: UUID = Depends(require_tenant),
    pagination: PaginationParams = Depends(get_pagination),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(require_permissions(SCHEDULES_VIEW)),
):
    return await AppointmentQueries(db).list_availability(
        tenant_id, offset=pagination.offset, limit=pagination.page_size
    )


@appointment_types_router.post("", response_model=AppointmentTypeRead, status_code=status.HTTP_201_CREATED)
async def create_appointment_type(
    body: AppointmentTypeCreate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    user: CurrentUser = Depends(require_permissions(APPOINTMENTS_MANAGE)),
):
    return await AppointmentCommands(db).create_type(tenant_id, body, actor=user)


@appointment_types_router.get("", response_model=list[AppointmentTypeRead])
async def list_appointment_types(
    tenant_id: UUID = Depends(require_tenant),
    pagination: PaginationParams = Depends(get_pagination),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(require_permissions(APPOINTMENTS_VIEW)),
):
    return await AppointmentQueries(db).list_types(
        tenant_id, offset=pagination.offset, limit=pagination.page_size
    )


@appointments_router.post("", response_model=AppointmentRead, status_code=status.HTTP_201_CREATED)
async def book_appointment(
    body: AppointmentCreate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    user: CurrentUser = Depends(require_permissions(APPOINTMENTS_MANAGE)),
):
    return await AppointmentCommands(db).book(tenant_id, body, actor=user)


@appointments_router.get("", response_model=AppointmentListResponse)
async def list_appointments(
    tenant_id: UUID = Depends(require_tenant),
    pagination: PaginationParams = Depends(get_pagination),
    spec: AppointmentListSpec = Depends(_appointment_spec),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(require_permissions(APPOINTMENTS_VIEW)),
):
    items, total = await AppointmentQueries(db).list_appointments(
        tenant_id, offset=pagination.offset, limit=pagination.page_size, spec=spec
    )
    return AppointmentListResponse(
        items=items, total=total, page=pagination.page, page_size=pagination.page_size
    )


@appointments_router.get("/{appointment_id}", response_model=AppointmentRead)
async def get_appointment(
    appointment_id: UUID,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(require_permissions(APPOINTMENTS_VIEW)),
):
    return await AppointmentQueries(db).get_appointment(tenant_id, appointment_id)


@appointments_router.post("/{appointment_id}/confirm", response_model=AppointmentRead)
async def confirm_appointment(
    appointment_id: UUID,
    body: AppointmentLifecycleRequest,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    user: CurrentUser = Depends(require_permissions(APPOINTMENTS_MANAGE)),
):
    return await AppointmentCommands(db).confirm(tenant_id, appointment_id, body, actor=user)


@appointments_router.post("/{appointment_id}/cancel", response_model=AppointmentRead)
async def cancel_appointment(
    appointment_id: UUID,
    body: AppointmentLifecycleRequest,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    user: CurrentUser = Depends(require_permissions(APPOINTMENTS_MANAGE)),
):
    return await AppointmentCommands(db).cancel(tenant_id, appointment_id, body, actor=user)


@appointments_router.post("/{appointment_id}/reschedule", response_model=AppointmentRead)
async def reschedule_appointment(
    appointment_id: UUID,
    body: AppointmentReschedule,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    user: CurrentUser = Depends(require_permissions(APPOINTMENTS_MANAGE)),
):
    return await AppointmentCommands(db).reschedule(tenant_id, appointment_id, body, actor=user)


@appointments_router.post("/{appointment_id}/no-show", response_model=AppointmentRead)
async def mark_no_show(
    appointment_id: UUID,
    body: AppointmentLifecycleRequest,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    user: CurrentUser = Depends(require_permissions(APPOINTMENTS_MANAGE)),
):
    return await AppointmentCommands(db).mark_no_show(tenant_id, appointment_id, body, actor=user)


@waiting_list_router.post("", response_model=WaitingListEntryRead, status_code=status.HTTP_201_CREATED)
async def create_waiting_list_entry(
    body: WaitingListEntryCreate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    user: CurrentUser = Depends(require_permissions(APPOINTMENTS_MANAGE)),
):
    return await AppointmentCommands(db).create_waiting_list(tenant_id, body, actor=user)


@waiting_list_router.get("", response_model=list[WaitingListEntryRead])
async def list_waiting_list(
    tenant_id: UUID = Depends(require_tenant),
    pagination: PaginationParams = Depends(get_pagination),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(require_permissions(APPOINTMENTS_VIEW)),
):
    return await AppointmentQueries(db).list_waiting_list(
        tenant_id, offset=pagination.offset, limit=pagination.page_size
    )
