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 InventoryRecordEntity, 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}/inventory", tags=["Inventory"])


class InventoryUpsertRequest(BaseModel):
    medication_id: UUID
    stock: int = Field(..., ge=0)
    reserved_stock: int = Field(0, ge=0)
    available_stock: int = Field(0, ge=0)
    min_stock: int | None = Field(None, ge=0)
    max_stock: int | None = Field(None, ge=0)
    last_updated_at: datetime | None = None
    source: str | None = Field(None, max_length=100)
    batch_id: str | None = Field(None, max_length=100)
    metadata: dict[str, Any] | None = None


class InventoryRecordResponse(BaseModel):
    id: UUID
    pharmacy_id: UUID
    medication_id: UUID
    stock: int
    reserved_stock: int
    available_stock: int
    min_stock: int | None
    max_stock: int | None
    last_updated_at: datetime | None
    source: str | None
    batch_id: str | None
    metadata: dict[str, Any] | None
    created_at: datetime | None
    updated_at: datetime | None


class InventoryListResponse(BaseModel):
    items: list[InventoryRecordResponse]
    total: int
    offset: int
    limit: int


class InventoryHistoryResponse(BaseModel):
    id: UUID
    pharmacy_id: UUID
    medication_id: UUID
    previous_stock: int
    new_stock: int
    delta: int
    reason: str | None
    source: str | None
    created_at: datetime | None


def _record_entity_to_response(e: InventoryRecordEntity) -> InventoryRecordResponse:
    return InventoryRecordResponse(
        id=e.id, pharmacy_id=e.pharmacy_id, medication_id=e.medication_id,
        stock=e.stock, reserved_stock=e.reserved_stock, available_stock=e.available_stock,
        min_stock=e.min_stock, max_stock=e.max_stock,
        last_updated_at=e.last_updated_at, source=e.source, batch_id=e.batch_id,
        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=InventoryListResponse)
async def list_inventory(
    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_inventory(pharmacy_id, offset=offset, limit=limit)
    return InventoryListResponse(items=[_record_entity_to_response(e) for e in entities], total=total, offset=offset, limit=limit)


@router.get("/{medication_id}", response_model=InventoryRecordResponse)
async def get_inventory_record(
    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_inventory_record(pharmacy_id, medication_id)
    if not entity:
        raise NotFoundException("InventoryRecord", f"{pharmacy_id}/{medication_id}")
    return _record_entity_to_response(entity)


@router.put("", response_model=InventoryRecordResponse)
async def upsert_inventory_record(
    pharmacy_id: UUID,
    body: InventoryUpsertRequest,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.OPERATOR)),
):
    service = _get_service(session)
    entity = InventoryRecordEntity(
        pharmacy_id=pharmacy_id, medication_id=body.medication_id,
        stock=body.stock, reserved_stock=body.reserved_stock,
        available_stock=body.available_stock, min_stock=body.min_stock,
        max_stock=body.max_stock, last_updated_at=body.last_updated_at,
        source=body.source, batch_id=body.batch_id, metadata_=body.metadata,
    )
    upserted = await service.upsert_inventory_record(entity)
    return _record_entity_to_response(upserted)


@router.get("/{medication_id}/history", response_model=list[InventoryHistoryResponse])
async def get_inventory_history(
    pharmacy_id: UUID,
    medication_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)
    history = await service.get_inventory_history(pharmacy_id, medication_id, offset=offset, limit=limit)
    return [
        InventoryHistoryResponse(
            id=h.id, pharmacy_id=h.pharmacy_id, medication_id=h.medication_id,
            previous_stock=h.previous_stock, new_stock=h.new_stock, delta=h.delta,
            reason=h.reason, source=h.source, created_at=h.created_at,
        )
        for h in history
    ]
