from django.db import transaction
from django.db.utils import IntegrityError
from django.test import TestCase

from oscar.core.loading import get_model

from ocyan.main.loading import get_ocyan_class

from ocyan.plugin.oscar_communications.oscar_invoices_extension.utils import (
    InvoiceCreator,
)

Order = get_model("order", "Order")

get_offline_request = get_ocyan_class("wagtail_utils.utils", "get_offline_request")


class InvoiceCreatorTest(TestCase):
    fixtures = [
        "communications/auth",
        "communications/address",
        "communications/catalogue",
        "communications/order",
        "communications/basket",
        "communications/customer",
        "communications/invoices",
    ]

    def check_sequence(self, invoice_creator):
        Invoice = invoice_creator.get_invoice_model()
        last_number = None
        for invoice in Invoice.objects.all():
            if last_number is not None:
                self.assertEqual(int(last_number) + 1, int(invoice.number))
            last_number = invoice.number

    def test_sequence(self):
        c = InvoiceCreator(request=get_offline_request("testserver"))
        Invoice = c.get_invoice_model()
        order = Order.objects.first()
        number = c.generate_invoice_number(order)
        self.assertEqual(number, "000001")
        with transaction.atomic():
            c.create_invoice_model(
                order=order, legal_entity=c.get_legal_entity(), number=number
            )
        self.assertEqual(Invoice.objects.count(), 1)
        self.check_sequence(c)

        second_order = Order.objects.all()[1]
        with transaction.atomic():
            with self.assertRaises(
                IntegrityError,
                msg="Using the same order number should raise IntegrityError",
            ):
                c.create_invoice_model(
                    order=second_order, legal_entity=c.get_legal_entity(), number=number
                )
        self.assertEqual(Invoice.objects.count(), 1)
        self.check_sequence(c)

        second_order = Order.objects.all()[1]
        with transaction.atomic():
            with self.assertRaises(
                IntegrityError,
                msg="Using the same order number should raise IntegrityError",
            ):
                c.create_invoice_model(
                    order=second_order, legal_entity=c.get_legal_entity(), number=number
                )
        self.assertEqual(Invoice.objects.count(), 1)
        self.check_sequence(c)

        with transaction.atomic():
            number = c.generate_invoice_number(second_order)
            c.create_invoice_model(
                order=second_order, legal_entity=c.get_legal_entity(), number=number
            )
            self.assertEqual(number, "000002")
        self.assertEqual(Invoice.objects.count(), 2)
        self.check_sequence(c)

        third_order = Order.objects.all()[2]
        with transaction.atomic():
            number = c.generate_invoice_number(third_order)
            c.create_invoice_model(
                order=third_order, legal_entity=c.get_legal_entity(), number=number
            )
            self.assertEqual(number, "000003")
        self.assertEqual(Invoice.objects.count(), 3)
        self.check_sequence(c)

    def test_get_or_create_invoice_sequence(self):
        c = InvoiceCreator(request=get_offline_request("testserver"))
        order = Order.objects.all()[3]
        invoice = c.get_or_create_invoice(order)
        self.assertEqual(invoice.number, "000001")
        invoice.number = "000002"
        invoice.save()
        second_order = Order.objects.all()[1]
        with self.assertRaises(
            IntegrityError,
            msg="Using the same order number should raise IntegrityError",
        ):
            c.get_or_create_invoice(second_order)
        self.check_sequence(c)
        invoice.number = "000001"
        invoice.save()

        second_invoice = c.get_or_create_invoice(second_order)
        self.assertEqual(second_invoice.number, "000002")
        self.check_sequence(c)

    def test_invoice_get_legal_entity(self):
        invoice_creator = InvoiceCreator(request=get_offline_request("testserver"))
        legal_entity_no_order = invoice_creator.get_legal_entity()
        self.assertEqual(
            legal_entity_no_order.pk,
            1,
            "If no order is passed, it will take the first legal entity by default.",
        )

        order_1 = Order.objects.first()
        self.assertEqual(order_1.site.name, "example.com")
        legal_entity_order1 = invoice_creator.get_legal_entity(order=order_1)
        self.assertEqual(
            legal_entity_order1.shop_name,
            "example.com",
            "A order has been passed, it should find the legal entity with this site name.",
        )

        order_2 = Order.objects.get(pk=4)
        self.assertEqual(order_2.site.name, "shop2")
        legal_entity_order2 = invoice_creator.get_legal_entity(order=order_2)
        self.assertEqual(
            legal_entity_order2.shop_name,
            "shop2",
            "A order has been passed, it should find the legal entity with this site name.",
        )
