# -*- coding: utf-8 -*-
# import json
from decimal import Decimal as D
from unittest.mock import patch

# from django.core.exceptions import ImproperlyConfigured
from django.contrib.auth import get_user_model
from django.test import RequestFactory, TestCase
from django.urls import reverse
from rest_framework.reverse import reverse as api_reverse

# from django.urls import reverse
from oscar.core.loading import get_model

from oscarapi.basket.operations import prepare_basket

import ocyan.core.fender

User = get_user_model()
Basket = get_model("basket", "Basket")
Product = get_model("catalogue", "Product")
ProductBased = get_model("shipping", "ProductBased")
factory = RequestFactory()

OCYAN_CONFIG = {
    "oscar": {
        "default_currency": "EUR",
        "tax_rates": ["zero", "low", "high"],
        "allow_anon_checkout": True,
        "use_price_incl_tax": True,
        "homepage": True,
    },
    "shipping": {
        "enable_free_shipping": True,
        "enable_weightbased_shipping": True,
        "enable_charged_shipping": True,
        "paid_shipping_first": True,
        "enable_product_based_shipping": True,
        "shipping_cost_attribute": "shipping_cost",
    },
}


@patch.object(ocyan.core.fender.config, "settings", OCYAN_CONFIG)
class TestShippingMethods(TestCase):
    fixtures = [
        "shipping/auth",
        "shipping/catalogue",
        "shipping/address",
        "shipping/shipping",
    ]

    def setUp(self):
        self.product = Product.objects.get(pk=1)
        self.product2 = Product.objects.get(pk=2)
        self.parent_product = Product.objects.get(pk=3)
        self.child_product = Product.objects.get(pk=4)

        self.SHIPPING_METHODS_DATA = {
            "country": "http://example.com/checkout/api/countries/NL/",
            "first_name": "dsfsdf",
            "house_number": "",
            "last_name": "sdfsdfsd",
            "line1": "Doctor Coppesstraat 29",
            "line2": "",
            "line3": "",
            "line4": "sdfsd",
            "notes": "",
            "phone_number": "",
            "postcode": "7523EJ",
            "state": "",
            "title": "",
        }

    def add_to_basket(self, product):
        return self.client.post(
            reverse("basket:add", args=(product.pk,)), {"quantity": 1}, follow=True
        )

    def test_product_based_shipping_method_with_product_attr_set(self):
        self.add_to_basket(self.product)

        url = api_reverse("checkoutapi:api-basket-shipping-methods", format="json")
        response = self.client.post(
            url,
            self.SHIPPING_METHODS_DATA,
            content_type="application/json; charset=UTF-8",
        )
        json_response = response.json()
        product_based_shipping_exists = False
        for method in json_response:
            if method["code"] == "productbased":
                product_based_shipping_exists = True

        self.assertTrue(
            product_based_shipping_exists,
            "Product based shipping is enabled in the ocyan.json and we have a product with the shipping attribute set in our basket.",
        )

    def test_product_based_shipping_method_without_product_attr_set(self):
        self.add_to_basket(self.product)

        self.product.attr.shipping_cost = None
        self.product.save()
        url = api_reverse("checkoutapi:api-basket-shipping-methods", format="json")
        response = self.client.post(
            url,
            self.SHIPPING_METHODS_DATA,
            content_type="application/json; charset=UTF-8",
        )
        json_response = response.json()
        product_based_shipping_exists = False
        for method in json_response:
            if method["code"] == "productbased":
                product_based_shipping_exists = True

        self.assertFalse(
            product_based_shipping_exists,
            "Product based shipping is enabled in the ocyan.json but we don't have any product in our basket with the shipping_cost attribute set.",
        )

    def test_product_based_only_shipping_field_is_false(self):
        self.add_to_basket(self.product)

        url = api_reverse("checkoutapi:api-basket-shipping-methods", format="json")
        response = self.client.post(
            url,
            self.SHIPPING_METHODS_DATA,
            content_type="application/json; charset=UTF-8",
        )
        json_response = response.json()
        self.assertTrue(
            len(json_response) > 2,
            "The field is_only_shipping_method is set to False, which means other shipping methods should be available",
        )

    def test_product_based_only_shipping_field_is_true(self):
        # Setting only shipping method to true
        product_based = ProductBased.objects.first()
        product_based.is_only_shipping_method = True
        product_based.save()
        self.add_to_basket(self.product)

        url = api_reverse("checkoutapi:api-basket-shipping-methods", format="json")
        response = self.client.post(
            url,
            self.SHIPPING_METHODS_DATA,
            content_type="application/json; charset=UTF-8",
        )
        json_response = response.json()
        self.assertEqual(
            len(json_response),
            1,
            "The field is_only_shipping_method is set to True, which means other shipping methods should not be available",
        )

    def test_product_based_use_highest_shipping_cost_field_true(self):
        self.add_to_basket(self.product)
        self.add_to_basket(self.product2)

        self.assertEqual(D(self.product.attr.shipping_cost), D(10))
        self.assertEqual(D(self.product2.attr.shipping_cost), D(1400))

        url = api_reverse("checkoutapi:api-basket-shipping-methods", format="json")
        response = self.client.post(
            url,
            self.SHIPPING_METHODS_DATA,
            content_type="application/json; charset=UTF-8",
        )
        json_response = response.json()

        product_based_shipping_price = None
        for method in json_response:
            if method["code"] == "productbased":
                product_based_shipping_price = D(method["price"]["incl_tax"])

        self.assertEqual(
            product_based_shipping_price,
            D(self.product2.attr.shipping_cost),
            "Product2 has the highest shipping_cost, so this one should be used.",
        )

    def test_product_based_use_highest_shipping_cost_field_false(self):
        # Setting highest_shipping_cost to false
        product_based = ProductBased.objects.first()
        product_based.use_highest_shipping_cost = False
        product_based.save()
        self.add_to_basket(self.product)
        self.add_to_basket(self.product2)

        self.assertEqual(D(self.product.attr.shipping_cost), D(10))
        self.assertEqual(D(self.product2.attr.shipping_cost), D(1400))

        url = api_reverse("checkoutapi:api-basket-shipping-methods", format="json")
        response = self.client.post(
            url,
            self.SHIPPING_METHODS_DATA,
            content_type="application/json; charset=UTF-8",
        )
        json_response = response.json()

        product_based_shipping_price = None
        for method in json_response:
            if method["code"] == "productbased":
                product_based_shipping_price = D(method["price"]["incl_tax"])

        self.assertEqual(
            product_based_shipping_price,
            D(self.product.attr.shipping_cost),
            "Product1 has the lowest shipping_cost, so this one should be used as the use_highest_shipping_cost field is set to False..",
        )

    def test_product_based_with_child_product(self):
        self.parent_product.attr.shipping_cost = D("10.00")
        self.parent_product.attr.save()

        self.add_to_basket(self.child_product)

        def get_shipping_price():
            url = api_reverse("checkoutapi:api-basket-shipping-methods", format="json")
            response = self.client.post(
                url,
                self.SHIPPING_METHODS_DATA,
                content_type="application/json; charset=UTF-8",
            )
            json_response = response.json()
            product_based_shipping_price = None
            for method in json_response:
                if method["code"] == "productbased":
                    product_based_shipping_price = D(method["price"]["incl_tax"])
            return product_based_shipping_price

        self.assertTrue(get_shipping_price(), D("10.00"))

        self.child_product.attr.shipping_cost = D("5.00")
        self.child_product.attr.save()

        self.assertTrue(get_shipping_price(), D("5.00"))


@patch.object(ocyan.core.fender.config, "settings", OCYAN_CONFIG)
class TestShippingTotalApi(TestCase):
    fixtures = [
        "shipping/address",
        "shipping/auth",
        "shipping/catalogue",
        "shipping/shipping",
        "shipping/basket",
    ]

    def test_basket_price(self):
        admin = User.objects.get(username="admin")
        self.client.force_login(admin)
        basket = admin.baskets.first()
        request = factory.get("/")
        request.user = admin
        request.session = self.client.session

        basket = prepare_basket(basket, request)
        self.assertEqual(basket.total_incl_tax, D("200"))

    # @patch("sorl.thumbnail.templatetags.thumbnail.ThumbnailNodeBase.render")


#     def test_total_default_shipping(self, render):
#         render.return_value = None
#         self.test_basket_price()
#
#         response = self.client.post(
#             reverse("checkoutapi:api-basket-total"),
#             json.dumps(
#                 {
#                     "shipping_address": {
#                         "country": "http://testserver/checkout/api/countries/NL/",
#                         "phone_number": "0612345678",
#                     }
#                 }
#             ),
#             content_type="application/json",
#         )
#         response = json.loads(response.content)
#         self.assertEqual(response["total"]["incl_tax"], "216.50")
#
#         response = self.client.post(
#             reverse("checkoutapi:api-basket-total"),
#             json.dumps(
#                 {
#                     "shipping_address": {
#                         "country": "http://testserver/checkout/api/countries/BE/",
#                         "phone_number": "+32466900873",
#                     }
#                 }
#             ),
#             content_type="application/json",
#         )
#         response = json.loads(response.content)
#         self.assertEqual(response["total"]["incl_tax"], "226.00")
#
#     @patch("sorl.thumbnail.templatetags.thumbnail.ThumbnailNodeBase.render")
#     def test_country_with_no_shipping_methods(self, render):
#         render.return_value = None
#         self.test_basket_price()
#
#         with self.assertRaises(ImproperlyConfigured):
#             response = self.client.post(
#                 reverse("checkoutapi:api-basket-total"),
#                 json.dumps(
#                     {
#                         "shipping_address": {
#                             "country": "http://testserver/checkout/api/countries/DE/",
#                             "phone_number": "+4930901820",
#                         }
#                     }
#                 ),
#                 content_type="application/json",
#             )
#             response = json.loads(response.content)
#
#     @patch("sorl.thumbnail.templatetags.thumbnail.ThumbnailNodeBase.render")
#     def test_total_shipping(self, render):
#         render.return_value = None
#         self.test_basket_price()
#         response = self.client.post(
#             reverse("checkoutapi:api-basket-total"),
#             json.dumps(
#                 {
#                     "shipping_address": {
#                         "country": "http://testserver/checkout/api/countries/NL/",
#                         "phone_number": "0612345678",
#                     },
#                     "shipping_method_code": "postnl",
#                 }
#             ),
#             content_type="application/json",
#         )
#         response = json.loads(response.content)
#         self.assertEqual(response["total"]["incl_tax"], "210.00")
#
#         response = self.client.post(
#             reverse("checkoutapi:api-basket-total"),
#             json.dumps(
#                 {
#                     "shipping_address": {
#                         "country": "http://testserver/checkout/api/countries/NL/",
#                         "phone_number": "0612345678",
#                     },
#                     "shipping_method_code": "weightnl",
#                 }
#             ),
#             content_type="application/json",
#         )
#         response = json.loads(response.content)
#         self.assertEqual(response["total"]["incl_tax"], "216.50")
#
#         response = self.client.post(
#             reverse("checkoutapi:api-basket-total"),
#             json.dumps(
#                 {
#                     "shipping_address": {
#                         "country": "http://testserver/checkout/api/countries/NL/",
#                         "phone_number": "0612345678",
#                     },
#                     "shipping_method_code": "both",
#                 }
#             ),
#             content_type="application/json",
#         )
#         response = json.loads(response.content)
#         self.assertEqual(response["total"]["incl_tax"], "212.00")
#
#         response = self.client.post(
#             reverse("checkoutapi:api-basket-total"),
#             json.dumps(
#                 {
#                     "shipping_address": {
#                         "country": "http://testserver/checkout/api/countries/BE/",
#                         "phone_number": "+32466900873",
#                     },
#                     "shipping_method_code": "both",
#                 }
#             ),
#             content_type="application/json",
#         )
#         response = json.loads(response.content)
#         self.assertEqual(response["total"]["incl_tax"], "212.00")
#
#         response = self.client.post(
#             reverse("checkoutapi:api-basket-total"),
#             json.dumps(
#                 {
#                     "shipping_address": {
#                         "country": "http://testserver/checkout/api/countries/BE/",
#                         "phone_number": "+32466900873",
#                     },
#                     "shipping_method_code": "postbe",
#                 }
#             ),
#             content_type="application/json",
#         )
#         response = json.loads(response.content)
#         self.assertEqual(response["total"]["incl_tax"], "390.00")
#
#         response = self.client.post(
#             reverse("checkoutapi:api-basket-total"),
#             json.dumps(
#                 {
#                     "shipping_address": {
#                         "country": "http://testserver/checkout/api/countries/BE/",
#                         "phone_number": "+32466900873",
#                     },
#                     "shipping_method_code": "weightbe",
#                 }
#             ),
#             content_type="application/json",
#         )
#         response = json.loads(response.content)
#         self.assertEqual(response["total"]["incl_tax"], "226.00")
#
#     @patch("sorl.thumbnail.templatetags.thumbnail.ThumbnailNodeBase.render")
#     def test_invalid_shipping_options(self, render):
#         render.return_value = None
#         self.test_basket_price()
#
#         response = self.client.post(
#             reverse("checkoutapi:api-basket-total"),
#             json.dumps(
#                 {
#                     "shipping_address": {
#                         "country": "http://testserver/checkout/api/countries/NL/",
#                         "phone_number": "0612345678",
#                     },
#                     "shipping_method_code": "runderlap",
#                 }
#             ),
#             content_type="application/json",
#         )
#         response = json.loads(response.content)
#         self.assertEqual(response["total"]["incl_tax"], "216.50")
#
#         response = self.client.post(
#             reverse("checkoutapi:api-basket-total"),
#             json.dumps(
#                 {
#                     "shipping_address": {
#                         "country": "http://testserver/checkout/api/countries/BE/",
#                         "phone_number": "+32466900873",
#                     },
#                     "shipping_method_code": "runderlap",
#                 }
#             ),
#             content_type="application/json",
#         )
#         response = json.loads(response.content)
#         self.assertEqual(response["total"]["incl_tax"], "226.00")
