"""
Appointment booking and slot management endpoints.

Handles lab appointment scheduling, time slot configurations, and appointment lifecycles.
"""

from datetime import date
from typing import Annotated
from uuid import UUID

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

from app.core.auth_deps import get_current_user, require_permission
from app.core.dependencies import get_db
from app.core.exceptions import ForbiddenException
from app.core.pagination import PaginationParams
from app.core.permissions import PermissionCode
from app.core.responses import PaginatedResponse, SuccessResponse
from app.models.appointment import AppointmentStatus, BookingChannel
from app.models.user import User
from app.repositories.lab_repository import LabRepository
from app.schemas.appointment import (
    AppointmentCreate,
    AppointmentRescheduleRequest,
    AppointmentResponse,
    AppointmentSlotCreate,
    AppointmentSlotResponse,
    AppointmentSlotUpdate,
    AppointmentUpdate,
    QuickAppointmentCreate,
)
from app.services.appointment_service import AppointmentService

router = APIRouter(tags=["Appointments"])
_service = AppointmentService()


async def _resolve_lab_id(db: AsyncSession, user: User, requested_lab_id: UUID | None) -> UUID:
    """Resolve the authenticated user's branch and reject staff-supplied cross-branch IDs."""
    if user.is_superadmin:
        if requested_lab_id:
            return requested_lab_id
        if user.lab_id:
            return user.lab_id
        return await LabRepository().get_default_lab_id(db)
    if not user.lab_id:
        return await LabRepository().get_default_lab_id(db)
    if requested_lab_id and requested_lab_id != user.lab_id:
        raise ForbiddenException("Access denied to another lab branch.")
    return user.lab_id


# ── Appointment Slots ─────────────────────────────────────────────────────────


@router.get(
    "/appointment-slots",
    response_model=PaginatedResponse[AppointmentSlotResponse],
    status_code=status.HTTP_200_OK,
    summary="List appointment slots",
    description="Retrieve available or configured appointment slots.",
    dependencies=[Depends(require_permission(PermissionCode.APPOINTMENT_READ))],
)
async def list_appointment_slots(
    db: Annotated[AsyncSession, Depends(get_db)],
    current_user: Annotated[User, Depends(get_current_user)],
    pagination: Annotated[PaginationParams, Depends()],
    lab_id: UUID | None = Query(None, description="Filter by lab branch ID"),
    is_active: bool | None = Query(None, description="Filter by active status"),
) -> PaginatedResponse[AppointmentSlotResponse]:
    target_lab_id = await _resolve_lab_id(db, current_user, lab_id)
    return await _service.list_slots(db, pagination, lab_id=target_lab_id, is_active=is_active)


@router.post(
    "/appointment-slots",
    response_model=SuccessResponse[AppointmentSlotResponse],
    status_code=status.HTTP_201_CREATED,
    summary="Create appointment slot",
    description="Define a new appointment time slot.",
    dependencies=[Depends(require_permission(PermissionCode.APPOINTMENT_WRITE))],
)
async def create_appointment_slot(
    data: AppointmentSlotCreate,
    db: Annotated[AsyncSession, Depends(get_db)],
    current_user: Annotated[User, Depends(get_current_user)],
) -> SuccessResponse[AppointmentSlotResponse]:
    target_lab_id = await _resolve_lab_id(db, current_user, data.lab_id)
    slot = await _service.create_slot(db, data.model_copy(update={"lab_id": target_lab_id}))
    return SuccessResponse(data=slot, message="Appointment slot created successfully.")


@router.post(
    "/appointment-slots/ensure-day",
    response_model=SuccessResponse[list[AppointmentSlotResponse]],
    status_code=status.HTTP_200_OK,
    summary="Ensure default slots for a day",
    description="Creates standard collection windows for the given date when missing.",
    dependencies=[Depends(require_permission(PermissionCode.APPOINTMENT_WRITE))],
)
async def ensure_day_appointment_slots(
    db: Annotated[AsyncSession, Depends(get_db)],
    current_user: Annotated[User, Depends(get_current_user)],
    target_date: Annotated[date, Query(alias="date", description="Calendar date (YYYY-MM-DD)")],
    lab_id: UUID | None = Query(None, description="Branch lab ID"),
) -> SuccessResponse[list[AppointmentSlotResponse]]:
    target_lab_id = await _resolve_lab_id(db, current_user, lab_id)
    slots = await _service.ensure_day_slots(db, target_date, lab_id=target_lab_id)
    return SuccessResponse(data=slots, message="Day slots are ready for booking.")


@router.get(
    "/appointment-slots/{slot_id}",
    response_model=SuccessResponse[AppointmentSlotResponse],
    status_code=status.HTTP_200_OK,
    summary="Get appointment slot",
    description="Retrieve details of a specific appointment slot.",
    dependencies=[Depends(require_permission(PermissionCode.APPOINTMENT_READ))],
)
async def get_appointment_slot(
    slot_id: UUID,
    db: Annotated[AsyncSession, Depends(get_db)],
    current_user: Annotated[User, Depends(get_current_user)],
) -> SuccessResponse[AppointmentSlotResponse]:
    target_lab_id = await _resolve_lab_id(db, current_user, None)
    slot = await _service.get_slot(db, slot_id, lab_id=target_lab_id)
    return SuccessResponse(data=slot)


@router.patch(
    "/appointment-slots/{slot_id}",
    response_model=SuccessResponse[AppointmentSlotResponse],
    status_code=status.HTTP_200_OK,
    summary="Update appointment slot",
    description="Modify an appointment time slot.",
    dependencies=[Depends(require_permission(PermissionCode.APPOINTMENT_WRITE))],
)
async def update_appointment_slot(
    slot_id: UUID,
    data: AppointmentSlotUpdate,
    db: Annotated[AsyncSession, Depends(get_db)],
    current_user: Annotated[User, Depends(get_current_user)],
) -> SuccessResponse[AppointmentSlotResponse]:
    target_lab_id = await _resolve_lab_id(db, current_user, None)
    slot = await _service.update_slot(db, slot_id, data, lab_id=target_lab_id)
    return SuccessResponse(data=slot, message="Appointment slot updated successfully.")


@router.delete(
    "/appointment-slots/{slot_id}",
    response_model=SuccessResponse[dict[str, str]],
    status_code=status.HTTP_200_OK,
    summary="Delete appointment slot",
    description="Remove an appointment slot.",
    dependencies=[Depends(require_permission(PermissionCode.APPOINTMENT_WRITE))],
)
async def delete_appointment_slot(
    slot_id: UUID,
    db: Annotated[AsyncSession, Depends(get_db)],
    current_user: Annotated[User, Depends(get_current_user)],
) -> SuccessResponse[dict[str, str]]:
    target_lab_id = await _resolve_lab_id(db, current_user, None)
    await _service.delete_slot(db, slot_id, lab_id=target_lab_id)
    return SuccessResponse(data={"id": str(slot_id)}, message="Appointment slot deleted successfully.")


# ── Appointments ──────────────────────────────────────────────────────────────


@router.get(
    "/appointments",
    response_model=PaginatedResponse[AppointmentResponse],
    status_code=status.HTTP_200_OK,
    summary="List appointments",
    description="Retrieve paginated list of booked appointments.",
    dependencies=[Depends(require_permission(PermissionCode.APPOINTMENT_READ))],
)
async def list_appointments(
    db: Annotated[AsyncSession, Depends(get_db)],
    current_user: Annotated[User, Depends(get_current_user)],
    pagination: Annotated[PaginationParams, Depends()],
    lab_id: UUID | None = Query(None, description="Filter by lab branch ID"),
    apt_status: AppointmentStatus | None = Query(None, alias="status", description="Filter by status"),
    patient_id: UUID | None = Query(None, description="Filter by patient ID"),
    slot_id: UUID | None = Query(None, description="Filter by slot ID"),
    active_only: bool = Query(False, description="Only pending, confirmed, or checked-in bookings"),
    target_date: date | None = Query(None, alias="date", description="Clinic-local date (Asia/Kathmandu)"),
) -> PaginatedResponse[AppointmentResponse]:
    target_lab_id = await _resolve_lab_id(db, current_user, lab_id)
    return await _service.list_appointments(
        db,
        pagination,
        lab_id=target_lab_id,
        status=apt_status,
        patient_id=patient_id,
        slot_id=slot_id,
        active_only=active_only,
        target_date=target_date,
    )


@router.get(
    "/appointments/active",
    response_model=SuccessResponse[AppointmentResponse | None],
    status_code=status.HTTP_200_OK,
    summary="Get patient's active appointment",
    description="Returns pending, confirmed, or checked-in booking for a patient, if one exists.",
    dependencies=[Depends(require_permission(PermissionCode.APPOINTMENT_READ))],
)
async def get_active_appointment(
    db: Annotated[AsyncSession, Depends(get_db)],
    current_user: Annotated[User, Depends(get_current_user)],
    patient_id: Annotated[UUID, Query(description="Patient ID")],
    lab_id: UUID | None = Query(None, description="Optional lab branch scope"),
) -> SuccessResponse[AppointmentResponse | None]:
    target_lab_id = await _resolve_lab_id(db, current_user, lab_id)
    apt = await _service.get_active_appointment_for_patient(db, patient_id, lab_id=target_lab_id)
    return SuccessResponse(data=apt)


@router.post(
    "/appointments",
    response_model=SuccessResponse[AppointmentResponse],
    status_code=status.HTTP_201_CREATED,
    summary="Book appointment",
    description="Book a new lab or home collection appointment.",
    dependencies=[Depends(require_permission(PermissionCode.APPOINTMENT_WRITE))],
)
async def create_appointment(
    data: AppointmentCreate,
    db: Annotated[AsyncSession, Depends(get_db)],
    current_user: Annotated[User, Depends(get_current_user)],
) -> SuccessResponse[AppointmentResponse]:
    target_lab_id = await _resolve_lab_id(db, current_user, data.lab_id)
    apt = await _service.create_appointment(db, data.model_copy(update={"lab_id": target_lab_id}))
    return SuccessResponse(data=apt, message="Appointment booked successfully.")


@router.get(
    "/appointments/public/slots",
    response_model=SuccessResponse[list[AppointmentSlotResponse]],
    status_code=status.HTTP_200_OK,
    summary="List public booking slots",
    description="Retrieve available collection windows for a specific date without authentication.",
)
async def list_public_slots(
    db: Annotated[AsyncSession, Depends(get_db)],
    target_date: Annotated[date, Query(alias="date", description="Calendar date (YYYY-MM-DD)")],
    lab_id: UUID | None = Query(None, description="Branch lab ID"),
) -> SuccessResponse[list[AppointmentSlotResponse]]:
    holiday = await _service.get_holiday_for_date(db, target_date, lab_id=lab_id)
    slots = await _service.ensure_day_slots(db, target_date, lab_id=lab_id)
    msg = f"Lab is closed on this date ({holiday.title})." if holiday and holiday.is_closed else None
    return SuccessResponse(data=slots, message=msg)


@router.post(
    "/appointments/public/book",
    response_model=SuccessResponse[AppointmentResponse],
    status_code=status.HTTP_201_CREATED,
    summary="Public guest appointment booking",
    description=(
        "Book appointment from public booking portal, automatically registering"
        " or matching patient and dispatching confirmation email."
    ),
)
async def public_book_appointment(
    data: QuickAppointmentCreate,
    db: Annotated[AsyncSession, Depends(get_db)],
) -> SuccessResponse[AppointmentResponse]:
    data.booked_via = BookingChannel.ONLINE
    apt = await _service.quick_book_appointment(db, data)
    return SuccessResponse(data=apt, message="Appointment booked successfully. Confirmation email sent.")


@router.post(
    "/appointments/quick-book",
    response_model=SuccessResponse[AppointmentResponse],
    status_code=status.HTTP_201_CREATED,
    summary="Staff quick appointment booking",
    description="Quickly book appointment for existing or unregistered patient without leaving the desk.",
    dependencies=[Depends(require_permission(PermissionCode.APPOINTMENT_WRITE))],
)
async def staff_quick_book_appointment(
    data: QuickAppointmentCreate,
    db: Annotated[AsyncSession, Depends(get_db)],
    current_user: Annotated[User, Depends(get_current_user)],
) -> SuccessResponse[AppointmentResponse]:
    target_lab_id = await _resolve_lab_id(db, current_user, data.lab_id)
    data = data.model_copy(update={"lab_id": target_lab_id})
    if not data.booked_via or data.booked_via == BookingChannel.ONLINE:
        data.booked_via = BookingChannel.PHONE
    apt = await _service.quick_book_appointment(db, data)
    return SuccessResponse(data=apt, message="Appointment scheduled successfully.")


@router.get(
    "/appointments/{appointment_id}",
    response_model=SuccessResponse[AppointmentResponse],
    status_code=status.HTTP_200_OK,
    summary="Get appointment details",
    description="Retrieve details for a specific appointment.",
    dependencies=[Depends(require_permission(PermissionCode.APPOINTMENT_READ))],
)
async def get_appointment(
    appointment_id: UUID,
    db: Annotated[AsyncSession, Depends(get_db)],
    current_user: Annotated[User, Depends(get_current_user)],
) -> SuccessResponse[AppointmentResponse]:
    target_lab_id = await _resolve_lab_id(db, current_user, None)
    apt = await _service.get_appointment(db, appointment_id, lab_id=target_lab_id)
    return SuccessResponse(data=apt)


@router.patch(
    "/appointments/{appointment_id}",
    response_model=SuccessResponse[AppointmentResponse],
    status_code=status.HTTP_200_OK,
    summary="Update appointment",
    description="Update notes or scheduled time for an appointment.",
    dependencies=[Depends(require_permission(PermissionCode.APPOINTMENT_WRITE))],
)
async def update_appointment(
    appointment_id: UUID,
    data: AppointmentUpdate,
    db: Annotated[AsyncSession, Depends(get_db)],
    current_user: Annotated[User, Depends(get_current_user)],
) -> SuccessResponse[AppointmentResponse]:
    target_lab_id = await _resolve_lab_id(db, current_user, None)
    apt = await _service.update_appointment(db, appointment_id, data, lab_id=target_lab_id)
    return SuccessResponse(data=apt, message="Appointment updated successfully.")


@router.post(
    "/appointments/{appointment_id}/reschedule",
    response_model=SuccessResponse[AppointmentResponse],
    status_code=status.HTTP_200_OK,
    summary="Reschedule appointment",
    description="Move the patient to a different time slot.",
    dependencies=[Depends(require_permission(PermissionCode.APPOINTMENT_WRITE))],
)
async def reschedule_appointment(
    appointment_id: UUID,
    data: AppointmentRescheduleRequest,
    db: Annotated[AsyncSession, Depends(get_db)],
    current_user: Annotated[User, Depends(get_current_user)],
) -> SuccessResponse[AppointmentResponse]:
    target_lab_id = await _resolve_lab_id(db, current_user, None)
    apt = await _service.reschedule_appointment(db, appointment_id, data.slot_id, lab_id=target_lab_id)
    return SuccessResponse(data=apt, message="Appointment rescheduled successfully.")


@router.post(
    "/appointments/{appointment_id}/confirm",
    response_model=SuccessResponse[AppointmentResponse],
    status_code=status.HTTP_200_OK,
    summary="Confirm appointment",
    description="Transition appointment status to CONFIRMED.",
    dependencies=[Depends(require_permission(PermissionCode.APPOINTMENT_WRITE))],
)
async def confirm_appointment(
    appointment_id: UUID,
    db: Annotated[AsyncSession, Depends(get_db)],
    current_user: Annotated[User, Depends(get_current_user)],
) -> SuccessResponse[AppointmentResponse]:
    target_lab_id = await _resolve_lab_id(db, current_user, None)
    apt = await _service.confirm_appointment(db, appointment_id, lab_id=target_lab_id)
    return SuccessResponse(data=apt, message="Appointment confirmed successfully.")


@router.post(
    "/appointments/{appointment_id}/complete",
    response_model=SuccessResponse[AppointmentResponse],
    status_code=status.HTTP_200_OK,
    summary="Complete appointment",
    description="Transition appointment status to COMPLETED.",
    dependencies=[Depends(require_permission(PermissionCode.APPOINTMENT_WRITE))],
)
async def complete_appointment(
    appointment_id: UUID,
    db: Annotated[AsyncSession, Depends(get_db)],
    current_user: Annotated[User, Depends(get_current_user)],
) -> SuccessResponse[AppointmentResponse]:
    target_lab_id = await _resolve_lab_id(db, current_user, None)
    apt = await _service.complete_appointment(db, appointment_id, lab_id=target_lab_id)
    return SuccessResponse(data=apt, message="Appointment completed successfully.")


@router.post(
    "/appointments/{appointment_id}/cancel",
    response_model=SuccessResponse[AppointmentResponse],
    status_code=status.HTTP_200_OK,
    summary="Cancel appointment",
    description="Cancel a scheduled appointment.",
    dependencies=[Depends(require_permission(PermissionCode.APPOINTMENT_WRITE))],
)
async def cancel_appointment(
    appointment_id: UUID,
    db: Annotated[AsyncSession, Depends(get_db)],
    current_user: Annotated[User, Depends(get_current_user)],
) -> SuccessResponse[AppointmentResponse]:
    target_lab_id = await _resolve_lab_id(db, current_user, None)
    apt = await _service.cancel_appointment(db, appointment_id, lab_id=target_lab_id)
    return SuccessResponse(data=apt, message="Appointment cancelled successfully.")


@router.post(
    "/appointments/{appointment_id}/check-in",
    response_model=SuccessResponse[AppointmentResponse],
    status_code=status.HTTP_200_OK,
    summary="Check in appointment",
    description="Mark the patient as arrived (checked_in) for reception processing.",
    dependencies=[Depends(require_permission(PermissionCode.APPOINTMENT_WRITE))],
)
async def check_in_appointment(
    appointment_id: UUID,
    db: Annotated[AsyncSession, Depends(get_db)],
    current_user: Annotated[User, Depends(get_current_user)],
) -> SuccessResponse[AppointmentResponse]:
    target_lab_id = await _resolve_lab_id(db, current_user, None)
    apt = await _service.check_in_appointment(db, appointment_id, lab_id=target_lab_id)
    return SuccessResponse(data=apt, message="Patient checked in successfully.")
