from abc import ABC, abstractmethod
from enum import StrEnum
from typing import ClassVar, TYPE_CHECKING

from pydantic import BaseModel, Field
from pydantic_settings import BaseSettings

from shipaw.models.base import ShipawBaseModel
from shipaw.utils.consts_enums import PackageFormat, ShipDirection
from shipaw.models.shipment import Shipment

if TYPE_CHECKING:
    from shipaw.fapi.responses import CompletedShipmentResponse
    from shipaw.fapi.requests import ShipmentRequest


class ProviderName(StrEnum):
    ROYAL_MAIL = 'ROYAL_MAIL'
    APC = 'APC'


class HasServiceCodes(ABC):
    service_codes_type: ClassVar[type[StrEnum]]
    default_service: ClassVar[StrEnum]

    @classmethod
    def lookup_service(cls, service_name: str):
        return cls.service_codes_type[service_name]

    @classmethod
    def reverse_lookup_service(cls, service_code: str):
        return cls.service_codes_type(service_code).name

    @classmethod
    def services_as_tups(cls) -> list[tuple[str, str]]:
        return [(e.name, str(e.value)) for e in cls.service_codes_type]

    @classmethod
    def services_as_dict(cls) -> dict[str, str]:
        return dict(cls.service_codes_type.__members__.items())


class ShippingProvider(HasServiceCodes, ABC, ShipawBaseModel):
    name: ClassVar[ProviderName]
    settings: BaseSettings
    settings_type: ClassVar[type[BaseSettings]]
    valid_directions: ClassVar[list[ShipDirection]]
    valid_direction_services: ClassVar[dict[ShipDirection, list[StrEnum]]] = Field(default_factory=dict)
    valid_direction_formats: ClassVar[dict[ShipDirection, list[PackageFormat]]] = Field(default_factory=dict)

    @property
    @abstractmethod
    def client(self):
        raise NotImplementedError

    @abstractmethod
    def is_sandbox(self) -> bool: ...

    @staticmethod
    @abstractmethod
    def provider_shipment(shipment: Shipment, service_code: StrEnum) -> BaseModel:
        """Takes Shipaw's agnostic Shipment object and returns provider-specific Shipment object"""
        ...

    @staticmethod
    @abstractmethod
    def agnostic_shipment(shipment: BaseModel) -> Shipment:
        """Takes provider-specific Shipment object and returns Shipaw's agnostic Shipment object"""
        ...

    @staticmethod
    @abstractmethod
    def book_shipment_request(shipment_request: 'ShipmentRequest') -> 'CompletedShipmentResponse': ...
