from decimal import Decimal as D

from django.db import models
from django.utils.translation import gettext_lazy as _

from oscar.core import prices
from oscar.core.loading import get_class, get_classes
from oscar.models.fields import AutoSlugField

from ocyan.core.fender import config
from ocyan.core.utils import round_to_two_dec
from ocyan.plugin.oscar.constants import TAX_RATE_ZERO

IncludingTaxPriceSelector = get_class("partner.taxbase", "IncludingTaxPriceSelector")
ExcludingTaxPriceSelector = get_class("partner.taxbase", "ExcludingTaxPriceSelector")

taxed_shipping_charge = get_class("shipping.utils", "taxed_shipping_charge")

tax_rate_as_fraction, get_default_tax_rate, ALL_TAX_RATES = get_classes(
    "taxes.taxrate",
    [
        "tax_rate_as_fraction",
        "get_default_tax_rate",
        "ALL_TAX_RATES",
    ],
)

USE_PRICE_INCL_TAX = config.get("oscar", "use_price_incl_tax", True)


class PaymentMethod(models.Model):
    country = models.ManyToManyField("address.Country", verbose_name=_("Countries"))
    label = models.CharField(max_length=32)
    key = models.CharField(max_length=32)
    send_invoice = models.BooleanField(
        default=config.get("oscar_communications", "invoice", False)
    )
    requires_permission = models.BooleanField(
        default=False,
        help_text="Only users with the 'Allow <name of this payment method>' permission are allowed to use this paymentmethod",
    )
    min_amount = models.DecimalField(
        _("Minimum basket amount"),
        decimal_places=2,
        max_digits=12,
        default=D("0.00"),
        help_text=_("The minimum amount before this payment is available"),
    )
    max_amount = models.DecimalField(
        _("Maximum basket amount"),
        decimal_places=2,
        max_digits=12,
        null=True,
        blank=True,
        help_text=_(
            "Some payment methods like pay after have a maximum amount to protect the merchant. If the basket contains more then this amount, the payment method is not available"
        ),
    )
    ordering = models.IntegerField(null=True, default=0)

    def has_permission(self, user):
        return not self.requires_permission or user.has_perm("checkout.%s" % self.key)

    def __str__(self):
        return self.label

    class Meta:
        ordering = ["ordering", "label"]


class AbstractSurcharge(models.Model):
    name = models.CharField(max_length=32, null=False)
    code = AutoSlugField(_("Code"), max_length=128, unique=True, populate_from="name")
    country = models.ManyToManyField(
        "address.Country", blank=True, verbose_name=_("Countries")
    )
    payment_method = models.ManyToManyField(
        "checkout.PaymentMethod",
        blank=True,
        verbose_name=_("Payment methods"),
    )
    vat_rate = models.CharField(
        _("VAT rate"),
        max_length=5,
        choices=ALL_TAX_RATES,
        default=get_default_tax_rate(),
        null=True,
    )

    def __str__(self):
        return self.name

    class Meta:
        abstract = True


class PercentageSurcharge(AbstractSurcharge):
    percentage = models.DecimalField(
        decimal_places=2, max_digits=12, null=False, blank=False
    )
    include_shipping_charges = models.BooleanField(default=True)

    def calculate(self, basket, **kwargs):
        if not basket.is_empty:
            shipping_charge = kwargs.get("shipping_charge", None)

            if shipping_charge is not None and self.include_shipping_charges:
                total_incl_tax = basket.total_incl_tax + shipping_charge.incl_tax
            else:
                total_incl_tax = basket.total_incl_tax

            surcharge_incl_tax = round_to_two_dec(
                total_incl_tax * (self.percentage / D(100))
            )

            price = IncludingTaxPriceSelector(
                basket.owner,
                basket.strategy.request,
                currency=basket.currency,
                incl_tax=surcharge_incl_tax,
                vat_rate=self.vat_rate,
            )
        else:
            price = IncludingTaxPriceSelector(
                basket.owner,
                basket.strategy.request,
                currency=basket.currency,
                incl_tax=D(0),
                vat_rate=TAX_RATE_ZERO,
            )

        kwargs = {
            "currency": price.currency,
            "excl_tax": price.excl_tax,
            "incl_tax": price.incl_tax,
            "tax_code": price.tax_code,
        }

        return prices.Price(**kwargs)


class FixedSurcharge(AbstractSurcharge):
    price = models.DecimalField(
        decimal_places=2, max_digits=12, null=False, blank=False
    )

    def calculate(self, basket, **kwargs):
        if USE_PRICE_INCL_TAX:
            price = IncludingTaxPriceSelector(
                basket.owner,
                basket.strategy.request,
                currency=basket.currency,
                incl_tax=self.price,
                vat_rate=self.vat_rate,
            )
        else:
            price = ExcludingTaxPriceSelector(
                basket.owner,
                basket.strategy.request,
                currency=basket.currency,
                excl_tax=self.price,
                vat_rate=self.vat_rate,
            )

        kwargs = {
            "currency": price.currency,
            "excl_tax": price.excl_tax,
            "incl_tax": price.incl_tax,
            "tax_code": price.tax_code,
        }

        return prices.Price(**kwargs)


from oscar.apps.checkout.models import *  # noqa
