from django.conf import settings
from django.db import models
from django.utils.encoding import smart_str
from django.utils.translation import gettext_lazy as _

from oscar.apps.basket import abstract_models as oscar
from oscar.core.loading import get_class

option_display_value = get_class("basket.option", "option_display_value")


class AbstractLineAttribute(oscar.AbstractLineAttribute):
    def display_value(self):
        return option_display_value(self.value)

    class Meta(oscar.AbstractLineAttribute.Meta):
        abstract = True


class AbstractLine(oscar.AbstractLine):
    # store tax with 4 decimals precision so we can have exact per line tax
    price_incl_tax = models.DecimalField(
        _("Price incl. Tax"), decimal_places=4, max_digits=12, null=True
    )

    @property
    def description(self):
        return smart_str(self.product)

    def get_warning(self):
        num_available = getattr(self.purchase_info.availability, "num_available", None)
        if (
            not self.purchase_info.availability.is_available_to_buy
            or not self.product.is_public  # pylint: disable=E1101
        ):
            msg = "'%(product)s' is no longer available"
            return _(msg) % {
                "product": self.product.get_title()  # pylint: disable=E1101
            }
        elif num_available is not None and num_available < self.quantity:
            msg = "Your basket contains %(quantity)s '%(product)s', but there are only %(num_available)s in stock."
            return _(msg) % {
                "product": self.product.get_title(),  # pylint: disable=E1101
                "quantity": self.quantity,
                "num_available": num_available,
            }

    class Meta(oscar.AbstractLine.Meta):
        abstract = True


class AbstractBasket(oscar.AbstractBasket):
    def get_stock_info(self, product, options):
        if getattr(settings, "OSCAR_STRATEGY_ACCEPTS_OPTIONS", False):
            return self.strategy.fetch_for_product(product=product, options=options)

        return super(AbstractBasket, self).get_stock_info(product, options)

    def merge(self, basket, add_quantities=True):
        allowed, _ = self.is_quantity_allowed(basket.num_items)
        if not allowed:  # empty before merge to avoid max quantity exceeding
            self.flush()

        super(AbstractBasket, self).merge(basket, add_quantities)

    @property
    def total_discount_excl_tax(self):
        return self.total_excl_tax_excl_discounts - self.total_excl_tax

    class Meta(oscar.AbstractBasket.Meta):
        abstract = True
