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

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

from src.domain.entities import PharmacyEntity
from src.domain.interfaces import IPharmacyRepository
from src.infrastructure.database.models import PharmacyModel


def _model_to_entity(m: PharmacyModel) -> PharmacyEntity:
    return PharmacyEntity(
        id=m.id,
        code=m.code,
        name=m.name,
        address=m.address,
        city=m.city,
        province=m.province,
        postal_code=m.postal_code,
        country=m.country,
        latitude=m.latitude,
        longitude=m.longitude,
        phone=m.phone,
        email=m.email,
        opening_hours=m.opening_hours,
        status=m.status,
        erp_type=m.erp_type,
        erp_version=m.erp_version,
        connector_id=m.connector_id,
        sync_strategy=m.sync_strategy,
        sync_interval_minutes=m.sync_interval_minutes,
        last_sync_at=m.last_sync_at,
        last_sync_status=m.last_sync_status,
        metadata_=m.metadata_,
        created_at=m.created_at,
        updated_at=m.updated_at,
    )


def _entity_to_model_data(e: PharmacyEntity) -> dict[str, Any]:
    return {
        "code": e.code,
        "name": e.name,
        "address": e.address,
        "city": e.city,
        "province": e.province,
        "postal_code": e.postal_code,
        "country": e.country,
        "latitude": e.latitude,
        "longitude": e.longitude,
        "phone": e.phone,
        "email": e.email,
        "opening_hours": e.opening_hours,
        "status": e.status,
        "erp_type": e.erp_type,
        "erp_version": e.erp_version,
        "connector_id": e.connector_id,
        "sync_strategy": e.sync_strategy,
        "sync_interval_minutes": e.sync_interval_minutes,
        "last_sync_at": e.last_sync_at,
        "last_sync_status": e.last_sync_status,
        "metadata_": e.metadata_,
    }


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

    async def get_by_id(self, pharmacy_id: uuid.UUID) -> PharmacyEntity | None:
        stmt = select(PharmacyModel).where(PharmacyModel.id == pharmacy_id)
        result = await self._session.execute(stmt)
        model = result.scalar_one_or_none()
        return _model_to_entity(model) if model else None

    async def get_by_code(self, code: str) -> PharmacyEntity | None:
        stmt = select(PharmacyModel).where(PharmacyModel.code == code)
        result = await self._session.execute(stmt)
        model = result.scalar_one_or_none()
        return _model_to_entity(model) if model else None

    async def list_pharmacies(
        self,
        *,
        status: str | None = None,
        erp_type: str | None = None,
        offset: int = 0,
        limit: int = 50,
    ) -> tuple[Sequence[PharmacyEntity], int]:
        filters: list[Any] = []
        if status:
            filters.append(PharmacyModel.status == status)
        if erp_type:
            filters.append(PharmacyModel.erp_type == erp_type)

        count_stmt = select(func.count()).select_from(PharmacyModel)
        if filters:
            count_stmt = count_stmt.where(*filters)
        total = (await self._session.execute(count_stmt)).scalar() or 0

        stmt = select(PharmacyModel).order_by(PharmacyModel.name).offset(offset).limit(limit)
        if filters:
            stmt = stmt.where(*filters)
        result = await self._session.execute(stmt)
        models = result.scalars().all()
        return [_model_to_entity(m) for m in models], total

    async def create(self, entity: PharmacyEntity) -> PharmacyEntity:
        model = PharmacyModel(**_entity_to_model_data(entity))
        self._session.add(model)
        await self._session.flush()
        return _model_to_entity(model)

    async def update(self, entity: PharmacyEntity) -> PharmacyEntity:
        data = _entity_to_model_data(entity)
        stmt = update(PharmacyModel).where(PharmacyModel.id == entity.id).values(**data)
        await self._session.execute(stmt)
        await self._session.flush()
        return await self.get_by_id(entity.id)  # type: ignore[return-value]

    async def delete(self, pharmacy_id: uuid.UUID) -> bool:
        stmt = delete(PharmacyModel).where(PharmacyModel.id == pharmacy_id)
        result = await self._session.execute(stmt)
        return result.rowcount > 0
