import uuid
from abc import ABC, abstractmethod
from collections.abc import Sequence
from typing import Any

from src.domain.entities import (
    APIClientEntity,
    APIKeyEntity,
    AuditLogEntity,
    ConnectorConfigEntity,
    ConnectorEntity,
    ConnectorVersionEntity,
    ERPSystemEntity,
    InventoryHistoryEntity,
    InventoryRecordEntity,
    MedicationCatalogEntryEntity,
    MedicationEntity,
    PharmacyEntity,
    ReservationEntity,
    SyncJobEntity,
    SyncLogEntity,
    TechnicalUserEntity,
)


class IPharmacyRepository(ABC):
    @abstractmethod
    async def get_by_id(self, pharmacy_id: uuid.UUID) -> PharmacyEntity | None: ...

    @abstractmethod
    async def get_by_code(self, code: str) -> PharmacyEntity | None: ...

    @abstractmethod
    async def list_pharmacies(
        self,
        *,
        status: str | None = None,
        erp_type: str | None = None,
        offset: int = 0,
        limit: int = 50,
    ) -> tuple[Sequence[PharmacyEntity], int]: ...

    @abstractmethod
    async def create(self, entity: PharmacyEntity) -> PharmacyEntity: ...

    @abstractmethod
    async def update(self, entity: PharmacyEntity) -> PharmacyEntity: ...

    @abstractmethod
    async def delete(self, pharmacy_id: uuid.UUID) -> bool: ...


class IERPSystemRepository(ABC):
    @abstractmethod
    async def get_by_id(self, erp_id: uuid.UUID) -> ERPSystemEntity | None: ...

    @abstractmethod
    async def list_erp_systems(self, *, erp_type: str | None = None, offset: int = 0, limit: int = 50) -> tuple[Sequence[ERPSystemEntity], int]: ...

    @abstractmethod
    async def create(self, entity: ERPSystemEntity) -> ERPSystemEntity: ...

    @abstractmethod
    async def update(self, entity: ERPSystemEntity) -> ERPSystemEntity: ...

    @abstractmethod
    async def delete(self, erp_id: uuid.UUID) -> bool: ...


class IConnectorRepository(ABC):
    @abstractmethod
    async def get_by_id(self, connector_id: uuid.UUID) -> ConnectorEntity | None: ...

    @abstractmethod
    async def list_connectors(self, *, erp_system_id: uuid.UUID | None = None, pharmacy_id: uuid.UUID | None = None, status: str | None = None, offset: int = 0, limit: int = 50) -> tuple[Sequence[ConnectorEntity], int]: ...

    @abstractmethod
    async def create(self, entity: ConnectorEntity) -> ConnectorEntity: ...

    @abstractmethod
    async def update(self, entity: ConnectorEntity) -> ConnectorEntity: ...

    @abstractmethod
    async def delete(self, connector_id: uuid.UUID) -> bool: ...


class IConnectorVersionRepository(ABC):
    @abstractmethod
    async def get_latest(self, connector_type: str) -> ConnectorVersionEntity | None: ...

    @abstractmethod
    async def list_versions(self, *, connector_type: str | None = None, offset: int = 0, limit: int = 50) -> tuple[Sequence[ConnectorVersionEntity], int]: ...

    @abstractmethod
    async def create(self, entity: ConnectorVersionEntity) -> ConnectorVersionEntity: ...


class IConnectorConfigRepository(ABC):
    @abstractmethod
    async def list_by_connector(self, connector_id: uuid.UUID) -> Sequence[ConnectorConfigEntity]: ...

    @abstractmethod
    async def get_by_key(self, connector_id: uuid.UUID, key: str) -> ConnectorConfigEntity | None: ...

    @abstractmethod
    async def upsert(self, entity: ConnectorConfigEntity) -> ConnectorConfigEntity: ...

    @abstractmethod
    async def delete(self, config_id: uuid.UUID) -> bool: ...


class IMedicationRepository(ABC):
    @abstractmethod
    async def get_by_id(self, medication_id: uuid.UUID) -> MedicationEntity | None: ...

    @abstractmethod
    async def get_by_nregistro(self, nregistro: str) -> MedicationEntity | None: ...

    @abstractmethod
    async def search(self, query: str, *, offset: int = 0, limit: int = 50) -> tuple[Sequence[MedicationEntity], int]: ...

    @abstractmethod
    async def create(self, entity: MedicationEntity) -> MedicationEntity: ...

    @abstractmethod
    async def update(self, entity: MedicationEntity) -> MedicationEntity: ...

    @abstractmethod
    async def delete(self, medication_id: uuid.UUID) -> bool: ...


class IMedicationCatalogRepository(ABC):
    @abstractmethod
    async def get_entry(self, pharmacy_id: uuid.UUID, medication_id: uuid.UUID) -> MedicationCatalogEntryEntity | None: ...

    @abstractmethod
    async def list_by_pharmacy(self, pharmacy_id: uuid.UUID, *, offset: int = 0, limit: int = 50) -> tuple[Sequence[MedicationCatalogEntryEntity], int]: ...

    @abstractmethod
    async def upsert(self, entity: MedicationCatalogEntryEntity) -> MedicationCatalogEntryEntity: ...


class IInventoryRepository(ABC):
    @abstractmethod
    async def get_record(self, pharmacy_id: uuid.UUID, medication_id: uuid.UUID) -> InventoryRecordEntity | None: ...

    @abstractmethod
    async def list_by_pharmacy(self, pharmacy_id: uuid.UUID, *, offset: int = 0, limit: int = 50) -> tuple[Sequence[InventoryRecordEntity], int]: ...

    @abstractmethod
    async def upsert(self, entity: InventoryRecordEntity) -> InventoryRecordEntity: ...

    @abstractmethod
    async def add_history(self, entity: InventoryHistoryEntity) -> InventoryHistoryEntity: ...

    @abstractmethod
    async def get_history(self, pharmacy_id: uuid.UUID, medication_id: uuid.UUID, *, offset: int = 0, limit: int = 50) -> Sequence[InventoryHistoryEntity]: ...


class IReservationRepository(ABC):
    @abstractmethod
    async def get_by_id(self, reservation_id: uuid.UUID) -> ReservationEntity | None: ...

    @abstractmethod
    async def get_by_idempotency_key(self, key: str) -> ReservationEntity | None: ...

    @abstractmethod
    async def list_by_pharmacy(self, pharmacy_id: uuid.UUID, *, status: str | None = None, offset: int = 0, limit: int = 50) -> tuple[Sequence[ReservationEntity], int]: ...

    @abstractmethod
    async def create(self, entity: ReservationEntity) -> ReservationEntity: ...

    @abstractmethod
    async def update(self, entity: ReservationEntity) -> ReservationEntity: ...


class ISyncJobRepository(ABC):
    @abstractmethod
    async def get_by_id(self, job_id: uuid.UUID) -> SyncJobEntity | None: ...

    @abstractmethod
    async def list_by_pharmacy(self, pharmacy_id: uuid.UUID, *, status: str | None = None, offset: int = 0, limit: int = 50) -> tuple[Sequence[SyncJobEntity], int]: ...

    @abstractmethod
    async def create(self, entity: SyncJobEntity) -> SyncJobEntity: ...

    @abstractmethod
    async def update(self, entity: SyncJobEntity) -> SyncJobEntity: ...


class ISyncLogRepository(ABC):
    @abstractmethod
    async def add(self, entity: SyncLogEntity) -> SyncLogEntity: ...

    @abstractmethod
    async def list_by_job(self, job_id: uuid.UUID, *, level: str | None = None, offset: int = 0, limit: int = 100) -> Sequence[SyncLogEntity]: ...


class IAuditLogRepository(ABC):
    @abstractmethod
    async def add(self, entity: AuditLogEntity) -> AuditLogEntity: ...

    @abstractmethod
    async def list_logs(self, *, actor_id: uuid.UUID | None = None, action: str | None = None, resource_type: str | None = None, offset: int = 0, limit: int = 50) -> tuple[Sequence[AuditLogEntity], int]: ...


class ITechnicalUserRepository(ABC):
    @abstractmethod
    async def get_by_id(self, user_id: uuid.UUID) -> TechnicalUserEntity | None: ...

    @abstractmethod
    async def get_by_username(self, username: str) -> TechnicalUserEntity | None: ...

    @abstractmethod
    async def list_users(self, *, role: str | None = None, is_active: bool | None = None, offset: int = 0, limit: int = 50) -> tuple[Sequence[TechnicalUserEntity], int]: ...

    @abstractmethod
    async def create(self, entity: TechnicalUserEntity, password_hash: str) -> TechnicalUserEntity: ...

    @abstractmethod
    async def update(self, entity: TechnicalUserEntity) -> TechnicalUserEntity: ...


class IAPIClientRepository(ABC):
    @abstractmethod
    async def get_by_id(self, client_id: uuid.UUID) -> APIClientEntity | None: ...

    @abstractmethod
    async def list_clients(self, *, is_active: bool | None = None, offset: int = 0, limit: int = 50) -> tuple[Sequence[APIClientEntity], int]: ...

    @abstractmethod
    async def create(self, entity: APIClientEntity) -> APIClientEntity: ...

    @abstractmethod
    async def update(self, entity: APIClientEntity) -> APIClientEntity: ...


class IAPIKeyRepository(ABC):
    @abstractmethod
    async def get_by_prefix(self, key_prefix: str) -> APIKeyEntity | None: ...

    @abstractmethod
    async def list_by_client(self, api_client_id: uuid.UUID) -> Sequence[APIKeyEntity]: ...

    @abstractmethod
    async def create(self, entity: APIKeyEntity) -> APIKeyEntity: ...

    @abstractmethod
    async def update_last_used(self, key_id: uuid.UUID) -> None: ...
