from datetime import datetime, timedelta
from decimal import Decimal as D
from unittest import mock

from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.cache import cache
from django.test import TestCase
from django.test.client import RequestFactory
from freezegun import freeze_time

from oscar.core.loading import get_class, get_model

import ocyan.core.fender

from ocyan.plugin.oscar_partner.partner.constants import LOCAL_VAT_APPLICABLE_KEY

Product = get_model("catalogue", "Product")
ProductClass = get_model("catalogue", "ProductClass")
Basket = get_model("basket", "Basket")
Order = get_model("order", "Order")
LocalEUVatSettings = get_model("oscar_partner", "LocalEUVatSettings")

Selector = get_class("partner.strategy", "Selector")

User = get_user_model()

EXCL_TAX_SETTINGS = {"oscar": {"use_price_incl_tax": False}}


class BaseLocalVatTest(TestCase):
    fixtures = ["partner/auth.json", "partner/catalogue.json"]

    def setUp(self):
        self.factory = RequestFactory()
        self.user = User.objects.get(username="henk")
        self.client.force_login(self.user)
        self.COUNTRY_OF_ORIGIN = settings.OSCAR_VAT_COUNTRY_OF_ORIGIN_CODE
        self.product = Product.objects.get(pk=1)
        self.eu_settings = LocalEUVatSettings.load()

    def treshold_exceeded(self):
        self.eu_settings.vat_threshold_exceeded = True
        self.eu_settings.save()
        cache.delete(LOCAL_VAT_APPLICABLE_KEY)

    def treshold_un_exceeded(self):
        self.eu_settings.vat_threshold_exceeded = False
        self.eu_settings.save()
        cache.delete(LOCAL_VAT_APPLICABLE_KEY)


class LocalVatExclTaxTest(BaseLocalVatTest):
    @mock.patch.object(ocyan.core.fender.config, "settings", EXCL_TAX_SETTINGS)
    def test_country_of_origin_without_treshold_exceeded(self):
        request = self.factory.get("/")
        request.META["shipping_country"] = self.COUNTRY_OF_ORIGIN

        strategy = Selector().strategy()
        strategy.user = self.user
        strategy.request = request
        purchase_info = strategy.fetch_for_product(self.product)

        self.assertEqual(purchase_info.price.excl_tax, 100)
        self.assertEqual(purchase_info.price.incl_tax, 121)

    @mock.patch.object(ocyan.core.fender.config, "settings", EXCL_TAX_SETTINGS)
    def test_country_of_origin_with_treshold_exceeded(self):
        self.treshold_exceeded()
        request = self.factory.get("/")
        request.META["shipping_country"] = self.COUNTRY_OF_ORIGIN

        strategy = Selector().strategy()
        strategy.user = self.user
        strategy.request = request
        purchase_info = strategy.fetch_for_product(self.product)

        self.assertEqual(purchase_info.price.excl_tax, 100)
        self.assertEqual(purchase_info.price.incl_tax, 121)

    @mock.patch.object(ocyan.core.fender.config, "settings", EXCL_TAX_SETTINGS)
    def test_dk_with_treshold_exceeded(self):
        self.treshold_exceeded()
        request = self.factory.get("/")
        request.META["shipping_country"] = "DK"

        strategy = Selector().strategy()
        strategy.user = self.user
        strategy.request = request
        purchase_info = strategy.fetch_for_product(self.product)

        self.assertEqual(purchase_info.price.excl_tax, 100)
        self.assertEqual(purchase_info.price.incl_tax, 125)

    @mock.patch.object(ocyan.core.fender.config, "settings", EXCL_TAX_SETTINGS)
    def test_dk_without_treshold_exceeded(self):
        self.treshold_un_exceeded()
        request = self.factory.get("/")
        request.META["shipping_country"] = "DK"

        strategy = Selector().strategy()
        strategy.user = self.user
        strategy.request = request
        purchase_info = strategy.fetch_for_product(self.product)

        self.assertEqual(purchase_info.price.excl_tax, 100)
        self.assertEqual(purchase_info.price.incl_tax, 121)


class LocalVatInclTaxTest(BaseLocalVatTest):
    def test_country_of_origin_without_treshold_exceeded(self):
        request = self.factory.get("/")
        request.META["shipping_country"] = self.COUNTRY_OF_ORIGIN

        strategy = Selector().strategy()
        strategy.user = self.user
        strategy.request = request
        purchase_info = strategy.fetch_for_product(self.product)

        self.assertEqual(purchase_info.price.excl_tax, D("82.6446"))
        self.assertEqual(purchase_info.price.incl_tax, 100)

    def test_country_of_origin_with_treshold_exceeded(self):
        self.treshold_exceeded()
        request = self.factory.get("/")
        request.META["shipping_country"] = self.COUNTRY_OF_ORIGIN

        strategy = Selector().strategy()
        strategy.user = self.user
        strategy.request = request
        purchase_info = strategy.fetch_for_product(self.product)

        self.assertEqual(purchase_info.price.excl_tax, D("82.6446"))
        self.assertEqual(purchase_info.price.incl_tax, 100)

    def test_dk_with_treshold_exceeded(self):
        self.treshold_exceeded()
        request = self.factory.get("/")
        request.META["shipping_country"] = "DK"

        strategy = Selector().strategy()
        strategy.user = self.user
        strategy.request = request
        purchase_info = strategy.fetch_for_product(self.product)

        self.assertEqual(purchase_info.price.excl_tax, D("82.6446"))
        self.assertEqual(purchase_info.price.incl_tax, D("103.3058"))

    def test_dk_without_treshold_exceeded(self):
        self.treshold_un_exceeded()
        request = self.factory.get("/")
        request.META["shipping_country"] = "DK"

        strategy = Selector().strategy()
        strategy.user = self.user
        strategy.request = request
        purchase_info = strategy.fetch_for_product(self.product)

        self.assertEqual(purchase_info.price.excl_tax, D("82.6446"))
        self.assertEqual(purchase_info.price.incl_tax, 100)


@freeze_time("2021-12-14 17:23:26")
class LocalVatSetterTest(BaseLocalVatTest):
    fixtures = ["partner/orders.json"] + BaseLocalVatTest.fixtures

    def test_treshold_is_set(self):
        yesterday = datetime.now() - timedelta(1)
        Order.objects.filter(pk=1).update(date_placed=yesterday)

        self.treshold_un_exceeded()
        self.assertEqual(self.eu_settings.vat_threshold_exceeded, False)

        request = self.factory.get("/")
        request.META["shipping_country"] = "FR"

        strategy = Selector().strategy()
        strategy.user = self.user
        strategy.request = request
        strategy.fetch_for_product(self.product)

        self.assertTrue(cache.get(LOCAL_VAT_APPLICABLE_KEY))

        self.eu_settings.refresh_from_db()
        self.assertEqual(self.eu_settings.vat_threshold_exceeded, False)
