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 ConflictException, NotFoundException
from src.domain.entities import MedicationEntity, 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="/medications", tags=["Medications"])


class MedicationCreateRequest(BaseModel):
    nregistro: str = Field(..., min_length=1, max_length=50)
    name: str = Field(..., min_length=1, max_length=500)
    active_ingredient: str | None = Field(None, max_length=500)
    dosage: str | None = Field(None, max_length=100)
    form: str | None = Field(None, max_length=100)
    atc_code: str | None = Field(None, max_length=20)
    prescription_required: bool = False
    manufacturer: str | None = Field(None, max_length=255)
    metadata: dict[str, Any] | None = None


class MedicationUpdateRequest(BaseModel):
    name: str | None = Field(None, min_length=1, max_length=500)
    active_ingredient: str | None = Field(None, max_length=500)
    dosage: str | None = Field(None, max_length=100)
    form: str | None = Field(None, max_length=100)
    atc_code: str | None = Field(None, max_length=20)
    prescription_required: bool | None = None
    manufacturer: str | None = Field(None, max_length=255)
    metadata: dict[str, Any] | None = None


class MedicationResponse(BaseModel):
    id: UUID
    nregistro: str
    name: str
    active_ingredient: str | None
    dosage: str | None
    form: str | None
    atc_code: str | None
    prescription_required: bool
    manufacturer: str | None
    metadata: dict[str, Any] | None
    created_at: datetime | None
    updated_at: datetime | None


class MedicationListResponse(BaseModel):
    items: list[MedicationResponse]
    total: int
    offset: int
    limit: int


def _entity_to_response(e: MedicationEntity) -> MedicationResponse:
    return MedicationResponse(
        id=e.id, nregistro=e.nregistro, name=e.name,
        active_ingredient=e.active_ingredient, dosage=e.dosage,
        form=e.form, atc_code=e.atc_code,
        prescription_required=e.prescription_required,
        manufacturer=e.manufacturer, 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=MedicationListResponse)
async def list_medications(
    q: 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)
    if q:
        entities, total = await service.search_medications(q, offset=offset, limit=limit)
    else:
        from src.domain.entities import MedicationEntity
        all_entities, total = await service.search_medications("", offset=offset, limit=limit)
        entities = all_entities
    return MedicationListResponse(items=[_entity_to_response(e) for e in entities], total=total, offset=offset, limit=limit)


@router.get("/search", response_model=MedicationListResponse)
async def search_medications(
    q: str = Query(..., min_length=1),
    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.search_medications(q, offset=offset, limit=limit)
    return MedicationListResponse(items=[_entity_to_response(e) for e in entities], total=total, offset=offset, limit=limit)


@router.get("/nregistro/{nregistro}", response_model=MedicationResponse)
async def get_medication_by_nregistro(
    nregistro: str,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(get_current_user),
):
    service = _get_service(session)
    entity = await service.get_by_nregistro(nregistro)
    if not entity:
        raise NotFoundException("Medication", nregistro)
    return _entity_to_response(entity)


@router.get("/{medication_id}", response_model=MedicationResponse)
async def get_medication(
    medication_id: UUID,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(get_current_user),
):
    service = _get_service(session)
    entity = await service.get_medication(medication_id)
    if not entity:
        raise NotFoundException("Medication", str(medication_id))
    return _entity_to_response(entity)


@router.post("", response_model=MedicationResponse, status_code=201)
async def create_medication(
    body: MedicationCreateRequest,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.ADMIN)),
):
    service = _get_service(session)
    entity = MedicationEntity(
        nregistro=body.nregistro, name=body.name,
        active_ingredient=body.active_ingredient, dosage=body.dosage,
        form=body.form, atc_code=body.atc_code,
        prescription_required=body.prescription_required,
        manufacturer=body.manufacturer, metadata_=body.metadata,
    )
    created = await service.create_medication(entity)
    return _entity_to_response(created)


@router.put("/{medication_id}", response_model=MedicationResponse)
async def update_medication(
    medication_id: UUID,
    body: MedicationUpdateRequest,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.ADMIN)),
):
    service = _get_service(session)
    entity = await service.get_medication(medication_id)
    if not entity:
        raise NotFoundException("Medication", str(medication_id))

    update_data = body.model_dump(exclude_unset=True)
    if "metadata" in update_data:
        entity.metadata_ = update_data.pop("metadata")
    for key, value in update_data.items():
        setattr(entity, key, value)

    updated = await service.update_medication(entity)
    return _entity_to_response(updated)


@router.delete("/{medication_id}", status_code=204)
async def delete_medication(
    medication_id: UUID,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.ADMIN)),
):
    service = _get_service(session)
    deleted = await service.delete_medication(medication_id)
    if not deleted:
        raise NotFoundException("Medication", str(medication_id))
