import uuid
from collections.abc import Sequence
from typing import Any

from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession

from src.domain.entities import MedicationCatalogEntryEntity
from src.domain.interfaces import IMedicationCatalogRepository
from src.infrastructure.database.models import MedicationCatalogEntryModel


def _model_to_entity(m: MedicationCatalogEntryModel) -> MedicationCatalogEntryEntity:
    return MedicationCatalogEntryEntity(
        id=m.id,
        pharmacy_id=m.pharmacy_id,
        medication_id=m.medication_id,
        price=m.price,
        stock=m.stock,
        is_available=m.is_available,
        last_verified_at=m.last_verified_at,
        metadata_=m.metadata_,
        created_at=m.created_at,
        updated_at=m.updated_at,
    )


def _entity_to_model_data(e: MedicationCatalogEntryEntity) -> dict[str, Any]:
    return {
        "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_,
    }


class MedicationCatalogRepository(IMedicationCatalogRepository):
    def __init__(self, session: AsyncSession) -> None:
        self._session = session

    async def get_entry(self, pharmacy_id: uuid.UUID, medication_id: uuid.UUID) -> MedicationCatalogEntryEntity | None:
        stmt = select(MedicationCatalogEntryModel).where(
            MedicationCatalogEntryModel.pharmacy_id == pharmacy_id,
            MedicationCatalogEntryModel.medication_id == medication_id,
        )
        result = await self._session.execute(stmt)
        model = result.scalar_one_or_none()
        return _model_to_entity(model) if model else None

    async def list_by_pharmacy(self, pharmacy_id: uuid.UUID, *, offset: int = 0, limit: int = 50) -> tuple[Sequence[MedicationCatalogEntryEntity], int]:
        count_stmt = select(func.count()).select_from(MedicationCatalogEntryModel).where(
            MedicationCatalogEntryModel.pharmacy_id == pharmacy_id
        )
        total = (await self._session.execute(count_stmt)).scalar() or 0

        stmt = (
            select(MedicationCatalogEntryModel)
            .where(MedicationCatalogEntryModel.pharmacy_id == pharmacy_id)
            .order_by(MedicationCatalogEntryModel.is_available.desc(), MedicationCatalogEntryModel.price.asc())
            .offset(offset)
            .limit(limit)
        )
        result = await self._session.execute(stmt)
        models = result.scalars().all()
        return [_model_to_entity(m) for m in models], total

    async def upsert(self, entity: MedicationCatalogEntryEntity) -> MedicationCatalogEntryEntity:
        existing = await self.get_entry(entity.pharmacy_id, entity.medication_id)
        if existing:
            from sqlalchemy import update
            data = _entity_to_model_data(entity)
            stmt = (
                update(MedicationCatalogEntryModel)
                .where(
                    MedicationCatalogEntryModel.pharmacy_id == entity.pharmacy_id,
                    MedicationCatalogEntryModel.medication_id == entity.medication_id,
                )
                .values(**data)
            )
            await self._session.execute(stmt)
            await self._session.flush()
            return await self.get_entry(entity.pharmacy_id, entity.medication_id)  # type: ignore[return-value]
        else:
            model = MedicationCatalogEntryModel(**_entity_to_model_data(entity))
            self._session.add(model)
            await self._session.flush()
            return _model_to_entity(model)
