from datetime import datetime
from typing import Any
from uuid import UUID

from fastapi import APIRouter, Depends, Query
from pydantic import BaseModel, Field
from sqlalchemy.ext.asyncio import AsyncSession

from src.api.v1.dependencies.auth import get_current_user, require_role
from src.core.exceptions import NotFoundException, ValidationException
from src.domain.entities import ReservationEntity, ReservationStatus, UserRole
from src.infrastructure.database.repositories import InventoryRepository, ReservationRepository
from src.infrastructure.database.session import get_db_session
from src.services.auth.security import TokenData
from src.services.inventory.inventory_service import InventoryService

router = APIRouter(prefix="/pharmacies/{pharmacy_id}/reservations", tags=["Reservations"])


class ReservationCreateRequest(BaseModel):
    medication_id: UUID
    quantity: int = Field(..., gt=0)
    external_reference: str | None = Field(None, max_length=255)
    idempotency_key: str | None = Field(None, max_length=255)
    ttl_minutes: int = Field(30, ge=1, le=1440)
    metadata: dict[str, Any] | None = None


class ReservationResponse(BaseModel):
    id: UUID
    pharmacy_id: UUID
    medication_id: UUID
    user_id: UUID | None
    external_reference: str | None
    quantity: int
    status: str
    expires_at: datetime | None
    confirmed_at: datetime | None
    cancelled_at: datetime | None
    idempotency_key: str | None
    metadata: dict[str, Any] | None
    created_at: datetime | None
    updated_at: datetime | None


class ReservationListResponse(BaseModel):
    items: list[ReservationResponse]
    total: int
    offset: int
    limit: int


def _entity_to_response(e: ReservationEntity) -> ReservationResponse:
    return ReservationResponse(
        id=e.id, pharmacy_id=e.pharmacy_id, medication_id=e.medication_id,
        user_id=e.user_id, external_reference=e.external_reference,
        quantity=e.quantity, status=e.status, expires_at=e.expires_at,
        confirmed_at=e.confirmed_at, cancelled_at=e.cancelled_at,
        idempotency_key=e.idempotency_key, metadata=e.metadata_,
        created_at=e.created_at, updated_at=e.updated_at,
    )


def _get_service(session: AsyncSession) -> InventoryService:
    return InventoryService(
        inventory_repo=InventoryRepository(session),
        reservation_repo=ReservationRepository(session),
    )


@router.get("", response_model=ReservationListResponse)
async def list_reservations(
    pharmacy_id: UUID,
    status: str | None = None,
    offset: int = Query(0, ge=0),
    limit: int = Query(50, ge=1, le=200),
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(get_current_user),
):
    service = _get_service(session)
    entities, total = await service.list_reservations(pharmacy_id, status=status, offset=offset, limit=limit)
    return ReservationListResponse(items=[_entity_to_response(e) for e in entities], total=total, offset=offset, limit=limit)


@router.get("/{reservation_id}", response_model=ReservationResponse)
async def get_reservation(
    pharmacy_id: UUID,
    reservation_id: UUID,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(get_current_user),
):
    service = _get_service(session)
    entity = await service.get_reservation(reservation_id)
    if not entity or entity.pharmacy_id != pharmacy_id:
        raise NotFoundException("Reservation", str(reservation_id))
    return _entity_to_response(entity)


@router.post("", response_model=ReservationResponse, status_code=201)
async def create_reservation(
    pharmacy_id: UUID,
    body: ReservationCreateRequest,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.OPERATOR)),
):
    service = _get_service(session)
    user_id = None
    if hasattr(current_user, "user_id"):
        user_id = current_user.user_id
    elif hasattr(current_user, "sub"):
        try:
            user_id = UUID(current_user.sub)
        except (ValueError, TypeError):
            pass

    created = await service.create_reservation(
        pharmacy_id=pharmacy_id,
        medication_id=body.medication_id,
        quantity=body.quantity,
        user_id=user_id,
        external_reference=body.external_reference,
        idempotency_key=body.idempotency_key,
        ttl_minutes=body.ttl_minutes,
    )
    return _entity_to_response(created)


@router.post("/{reservation_id}/confirm", response_model=ReservationResponse)
async def confirm_reservation(
    pharmacy_id: UUID,
    reservation_id: UUID,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.OPERATOR)),
):
    service = _get_service(session)
    updated = await service.confirm_reservation(reservation_id)
    return _entity_to_response(updated)


@router.post("/{reservation_id}/cancel", response_model=ReservationResponse)
async def cancel_reservation(
    pharmacy_id: UUID,
    reservation_id: UUID,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.OPERATOR)),
):
    service = _get_service(session)
    updated = await service.cancel_reservation(reservation_id)
    return _entity_to_response(updated)
