from oscar.apps.shipping.repository import Repository as BaseRepository
from oscar.core.loading import get_model

from ocyan.core.fender import config

OrderAndItemCharges = get_model("shipping", "OrderAndItemCharges")
WeightBased = get_model("shipping", "WeightBased")
ProductBased = get_model("shipping", "ProductBased")
Country = get_model("address", "Country")


class Repository(BaseRepository):
    @staticmethod
    def _country(shipping_addr):
        if hasattr(shipping_addr, "country"):
            return shipping_addr.country
        else:
            return shipping_addr.get("country")

    def get_weight_based_charges(self, shipping_addr):
        if shipping_addr is None:
            return WeightBased.objects.filter(
                countries=Country.objects.filter(is_shipping_country=True).first()
            )
        else:
            return WeightBased.objects.filter(countries=self._country(shipping_addr))

    def get_order_and_item_charges(self, shipping_addr):
        if shipping_addr is None:
            return OrderAndItemCharges.objects.filter(
                countries=Country.objects.filter(is_shipping_country=True).first()
            )
        else:
            return OrderAndItemCharges.objects.filter(
                countries=self._country(shipping_addr)
            )

    def get_product_based_charge(self, shipping_addr, basket):
        if shipping_addr is None:
            product_based_shipping = ProductBased.objects.filter(
                countries=Country.objects.filter(is_shipping_country=True).first()
            ).first()
        else:
            product_based_shipping = ProductBased.objects.filter(
                countries=self._country(shipping_addr)
            ).first()

        if product_based_shipping is None:
            return product_based_shipping

        price = product_based_shipping.calculate(basket)
        # It's possible products in the basket might not have this attribute set, in this case we return None for the price.
        if price is not None:
            return product_based_shipping

    def get_available_shipping_methods(self, basket, shipping_addr=None, **kwargs):
        """
        Return a list of all applicable shipping method instances for a given
        basket, address etc. This method is intended to be overridden.
        """
        methods = tuple()
        if config.get("shipping", "enable_free_shipping", False):
            methods = super(Repository, self).get_available_shipping_methods(
                basket, shipping_addr, **kwargs
            )
        if config.get("shipping", "enable_charged_shipping", False):
            methods += tuple(self.get_order_and_item_charges(shipping_addr))
        if config.get("shipping", "enable_weightbased_shipping", False):
            methods += tuple(self.get_weight_based_charges(shipping_addr))
        if config.get("shipping", "enable_product_based_shipping", False):
            product_based_charge = self.get_product_based_charge(shipping_addr, basket)
            if product_based_charge is not None:
                if product_based_charge.is_only_shipping_method:
                    methods = tuple([product_based_charge])
                else:
                    methods += tuple([product_based_charge])

        if config.get("shipping", "paid_shipping_first", False):
            return methods[::-1]

        return methods
