from typing import Optional

import odin

from oscar.core.loading import get_class, get_model

from ocyan.main.loading import get_baseclass

# resources
OrderResource = get_class("oscar_odin.resources.order", "OrderResource")
SourceTypeResource = get_class("oscar_odin.resources.order", "SourceTypeResource")
BillingAddressResource = get_class(
    "oscar_odin.resources.address", "BillingAddressResource"
)
ShippingAddressResource = get_class(
    "oscar_odin.resources.address", "ShippingAddressResource"
)

# mappings
BaseOrderToResource = get_baseclass(
    "oscar_odin.mappings.order", "OrderToResource", "ocyan.plugin.oscar_odin"
)
ShippingAddressToResource = get_class(
    "oscar_odin.mappings.address", "ShippingAddressToResource"
)
BillingAddressToResource = get_class(
    "oscar_odin.mappings.address", "BillingAddressToResource"
)

OrderModel = get_model("order", "Order")
SourceTypeModel = get_model("payment", "SourceType")


class SourceTypeToResource(odin.Mapping):
    """Mapping from source type model to resource"""

    from_obj = SourceTypeModel
    to_obj = SourceTypeResource


class OrderToResource(BaseOrderToResource):
    """Mapping from order model to resource."""

    from_obj = OrderModel
    to_obj = OrderResource

    @odin.assign_field
    def billing_address(self) -> Optional[BillingAddressResource]:
        """Map billing address."""
        if self.source.billing_address:
            return BillingAddressToResource.apply(self.source.billing_address)

    @odin.assign_field
    def shipping_address(self) -> Optional[ShippingAddressResource]:
        """Map shipping address."""
        if self.source.shipping_address:
            return ShippingAddressToResource.apply(self.source.shipping_address)

    @odin.assign_field
    def payment_source_type(self) -> Optional[SourceTypeResource]:
        """Map payment source type."""
        if self.source.payment_source_type:
            return SourceTypeToResource.apply(self.source.payment_source_type)

    @odin.assign_field(to_field="delivery_date")
    def delivery_date(self):
        # delivery_date is a property, so odin does not automatically map it even though it has the same name.
        return self.source.delivery_date
