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
from src.domain.entities import MedicationCatalogEntryEntity, UserRole
from src.infrastructure.database.repositories import MedicationCatalogRepository, MedicationRepository
from src.infrastructure.database.session import get_db_session
from src.services.auth.security import TokenData
from src.services.medication.medication_service import MedicationService

router = APIRouter(prefix="/pharmacies/{pharmacy_id}/catalog", tags=["Medication Catalog"])


class CatalogEntryUpsertRequest(BaseModel):
    medication_id: UUID
    price: float | None = None
    stock: int = 0
    is_available: bool = False
    last_verified_at: datetime | None = None
    metadata: dict[str, Any] | None = None


class CatalogEntryResponse(BaseModel):
    id: UUID
    pharmacy_id: UUID
    medication_id: UUID
    price: float | None
    stock: int
    is_available: bool
    last_verified_at: datetime | None
    metadata: dict[str, Any] | None
    created_at: datetime | None
    updated_at: datetime | None


class CatalogListResponse(BaseModel):
    items: list[CatalogEntryResponse]
    total: int
    offset: int
    limit: int


def _entity_to_response(e: MedicationCatalogEntryEntity) -> CatalogEntryResponse:
    return CatalogEntryResponse(
        id=e.id, pharmacy_id=e.pharmacy_id, medication_id=e.medication_id,
        price=e.price, stock=e.stock, is_available=e.is_available,
        last_verified_at=e.last_verified_at, metadata=e.metadata_,
        created_at=e.created_at, updated_at=e.updated_at,
    )


def _get_service(session: AsyncSession) -> MedicationService:
    return MedicationService(
        medication_repo=MedicationRepository(session),
        catalog_repo=MedicationCatalogRepository(session),
    )


@router.get("", response_model=CatalogListResponse)
async def list_catalog(
    pharmacy_id: UUID,
    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_catalog(pharmacy_id, offset=offset, limit=limit)
    return CatalogListResponse(items=[_entity_to_response(e) for e in entities], total=total, offset=offset, limit=limit)


@router.get("/{medication_id}", response_model=CatalogEntryResponse)
async def get_catalog_entry(
    pharmacy_id: UUID,
    medication_id: UUID,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(get_current_user),
):
    service = _get_service(session)
    entity = await service.get_catalog_entry(pharmacy_id, medication_id)
    if not entity:
        raise NotFoundException("CatalogEntry", f"{pharmacy_id}/{medication_id}")
    return _entity_to_response(entity)


@router.put("", response_model=CatalogEntryResponse)
async def upsert_catalog_entry(
    pharmacy_id: UUID,
    body: CatalogEntryUpsertRequest,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.OPERATOR)),
):
    service = _get_service(session)
    entity = MedicationCatalogEntryEntity(
        pharmacy_id=pharmacy_id, medication_id=body.medication_id,
        price=body.price, stock=body.stock, is_available=body.is_available,
        last_verified_at=body.last_verified_at, metadata_=body.metadata,
    )
    upserted = await service.upsert_catalog_entry(entity)
    return _entity_to_response(upserted)
