import pytest
from django.http import HttpResponse
from django.test import TestCase
from django.test.client import Client
from django.urls import reverse
from django.utils.timezone import localdate
from model_bakery import baker
from pytest_django.asserts import assertRedirects

from core.baker_recipes import subscriber_user
from core.models import Group, User
from counter.baker_recipes import product_recipe
from counter.models import Counter, ProductType, get_eboutic
from counter.tests.test_counter import BasketItem
from eboutic.models import Basket


@pytest.mark.django_db
def test_get_eboutic():
    assert Counter.objects.get(name="Eboutic") == get_eboutic()

    baker.make(Counter, type="EBOUTIC")

    assert Counter.objects.get(name="Eboutic") == get_eboutic()


class TestEboutic(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.group_cotiz = baker.make(Group)
        cls.group_public = baker.make(Group)

        cls.new_customer = baker.make(User)
        cls.new_customer_adult = baker.make(User)
        cls.subscriber = subscriber_user.make()

        cls.set_age(cls.new_customer, 5)
        cls.set_age(cls.new_customer_adult, 20)
        cls.set_age(cls.subscriber, 20)

        product_type = baker.make(ProductType)

        cls.snack = product_recipe.make(
            selling_price=1.5, special_selling_price=1, product_type=product_type
        )
        cls.beer = product_recipe.make(
            limit_age=18,
            selling_price=2.5,
            special_selling_price=1,
            product_type=product_type,
        )
        cls.not_in_counter = product_recipe.make(
            selling_price=3.5, product_type=product_type
        )
        cls.cotiz = product_recipe.make(selling_price=10, product_type=product_type)

        cls.group_public.products.add(cls.snack, cls.beer, cls.not_in_counter)
        cls.group_cotiz.products.add(cls.cotiz)

        cls.subscriber.groups.add(cls.group_cotiz, cls.group_public)
        cls.new_customer.groups.add(cls.group_public)
        cls.new_customer_adult.groups.add(cls.group_public)

        cls.eboutic = get_eboutic()
        cls.eboutic.products.add(cls.cotiz, cls.beer, cls.snack)

    @classmethod
    def set_age(cls, user: User, age: int):
        user.date_of_birth = localdate().replace(year=localdate().year - age)
        user.save()

    def submit_basket(
        self, basket: list[BasketItem], client: Client | None = None
    ) -> HttpResponse:
        used_client: Client = client if client is not None else self.client
        data = {
            "form-TOTAL_FORMS": str(len(basket)),
            "form-INITIAL_FORMS": "0",
        }
        for index, item in enumerate(basket):
            data.update(item.to_form(index))
        return used_client.post(reverse("eboutic:main"), data)

    def test_submit_empty_basket(self):
        self.client.force_login(self.subscriber)
        for basket in [
            [],
            [BasketItem(None, None)],
            [BasketItem(None, None), BasketItem(None, None)],
        ]:
            response = self.submit_basket(basket)
            assert response.status_code == 200
            assert "Votre panier est vide" in response.text

    def test_submit_invalid_basket(self):
        self.client.force_login(self.subscriber)
        for item in [
            BasketItem(-1, 2),
            BasketItem(self.snack.id, -1),
            BasketItem(None, 1),
            BasketItem(self.snack.id, None),
        ]:
            response = self.submit_basket([item])
            assert response.status_code == 200

    def test_anonymous(self):
        assertRedirects(
            self.client.get(reverse("eboutic:main")),
            reverse("core:login", query={"next": reverse("eboutic:main")}),
        )
        assertRedirects(
            self.submit_basket([]),
            reverse("core:login", query={"next": reverse("eboutic:main")}),
        )
        assertRedirects(
            self.submit_basket([BasketItem(self.snack.id, 1)]),
            reverse("core:login", query={"next": reverse("eboutic:main")}),
        )

    def test_add_forbidden_product(self):
        self.client.force_login(self.new_customer)
        response = self.submit_basket([BasketItem(self.beer.id, 1)])
        assert response.status_code == 200
        assert Basket.objects.first() is None

        response = self.submit_basket([BasketItem(self.cotiz.id, 1)])
        assert response.status_code == 200
        assert Basket.objects.first() is None

        response = self.submit_basket([BasketItem(self.not_in_counter.id, 1)])
        assert response.status_code == 200
        assert Basket.objects.first() is None

        self.client.force_login(self.new_customer)
        response = self.submit_basket([BasketItem(self.cotiz.id, 1)])
        assert response.status_code == 200
        assert Basket.objects.first() is None

        response = self.submit_basket([BasketItem(self.not_in_counter.id, 1)])
        assert response.status_code == 200
        assert Basket.objects.first() is None

    def test_create_basket(self):
        self.client.force_login(self.new_customer)
        assertRedirects(
            self.submit_basket([BasketItem(self.snack.id, 2)]),
            reverse("eboutic:checkout", kwargs={"basket_id": 1}),
        )
        assert Basket.objects.get(id=1).total == self.snack.selling_price * 2

        self.client.force_login(self.new_customer_adult)
        assertRedirects(
            self.submit_basket(
                [BasketItem(self.snack.id, 2), BasketItem(self.beer.id, 1)]
            ),
            reverse("eboutic:checkout", kwargs={"basket_id": 2}),
        )
        assert (
            Basket.objects.get(id=2).total
            == self.snack.selling_price * 2 + self.beer.selling_price
        )

        self.client.force_login(self.subscriber)
        assertRedirects(
            self.submit_basket(
                [
                    BasketItem(self.snack.id, 2),
                    BasketItem(self.beer.id, 1),
                    BasketItem(self.cotiz.id, 1),
                ]
            ),
            reverse("eboutic:checkout", kwargs={"basket_id": 3}),
        )
        assert (
            Basket.objects.get(id=3).total
            == self.snack.selling_price * 2
            + self.beer.selling_price
            + self.cotiz.selling_price
        )