"""
Ensures that confirmations work with validators on the Model and on the Modelform.
"""

from importlib import reload
from unittest import mock

from selenium.webdriver.common.by import By

from admin_action_tools.constants import CONFIRM_ADD
from admin_action_tools.tests.helpers import AdminConfirmIntegrationTestCase
from tests.factories import (
    InventoryFactory,
    ItemFactory,
    ShopFactory,
    TransactionFactory,
)
from tests.market.admin import shoppingmall_admin
from tests.market.models import ItemSale


class ConfirmWithValidatorsTests(AdminConfirmIntegrationTestCase):
    def setUp(self):
        self.admin = shoppingmall_admin.ShoppingMallAdmin
        self.admin.inlines = [shoppingmall_admin.ShopInline]
        super().setUp()

    def tearDown(self):
        reload(shoppingmall_admin)
        super().tearDown()

    @mock.patch("tests.market.models.ItemSale.clean")
    def test_can_confirm_for_models_with_validator_on_model_field(self, _mock_clean):
        # ItemSale.currency has a validator on it
        ItemFactory()
        TransactionFactory()

        self.selenium.get(self.live_server_url + "/admin/market/itemsale/add/")
        # Should ask for confirmation of change
        self.assertIn(CONFIRM_ADD, self.selenium.page_source)

        self.set_value(by=By.NAME, by_value="quantity", value="1")
        self.set_value(by=By.NAME, by_value="total", value="10.00")
        self.set_value(by=By.NAME, by_value="currency", value="USD")
        self.select_index(by=By.NAME, by_value="transaction", index=1)
        self.select_index(by=By.NAME, by_value="item", index=1)

        self.selenium.find_element(By.NAME, "_continue").click()

        # Should have hidden form containing the updated name
        self.assertIn("Confirm", self.selenium.page_source)
        hidden_form = self.selenium.find_element(By.ID, "hidden-form")
        currency = hidden_form.find_element(By.NAME, "currency")
        self.assertIn("USD", currency.get_attribute("value"))

        # Should not have been added yet
        self.assertEqual(ItemSale.objects.count(), 0)
        self.selenium.find_element(By.NAME, "_continue").click()

        # Should persist change
        self.assertEqual(ItemSale.objects.count(), 1)

    def test_cannot_confirm_for_models_with_validator_on_model_field_if_validator_fails(
        self,
    ):
        # ItemSale.currency has a validator on it
        shop = ShopFactory()
        item = ItemFactory()
        InventoryFactory(shop=shop, item=item, quantity=10)
        TransactionFactory(shop=shop)

        self.selenium.get(self.live_server_url + "/admin/market/itemsale/add/")
        # Should ask for confirmation of change
        self.assertIn(CONFIRM_ADD, self.selenium.page_source)

        self.set_value(by=By.NAME, by_value="quantity", value="1")
        self.set_value(by=By.NAME, by_value="total", value="10.00")
        self.set_value(by=By.NAME, by_value="currency", value="FAKE")
        self.select_index(by=By.NAME, by_value="transaction", index=1)
        self.select_index(by=By.NAME, by_value="item", index=1)

        self.selenium.find_element(By.NAME, "_continue").click()

        # Should show errors and not confirmation page
        self.assertNotIn("Confirm", self.selenium.page_source)
        self.assertIn("Invalid Currency", self.selenium.page_source)
        self.assertIn(CONFIRM_ADD, self.selenium.page_source)

        # Should not have been added yet
        self.assertEqual(ItemSale.objects.count(), 0)

        # Now fix the issue
        self.set_value(by=By.NAME, by_value="currency", value="USD")
        self.selenium.find_element(By.NAME, "_continue").click()

        # Should have hidden form containing the updated currency
        self.assertIn("Confirm", self.selenium.page_source)
        hidden_form = self.selenium.find_element(By.ID, "hidden-form")
        currency = hidden_form.find_element(By.NAME, "currency")
        self.assertIn("USD", currency.get_attribute("value"))

        # Should not have been added yet
        self.assertEqual(ItemSale.objects.count(), 0)
        self.selenium.find_element(By.NAME, "_continue").click()

        # Should persist change
        self.assertEqual(ItemSale.objects.count(), 1)

    def test_can_confirm_for_models_with_clean_overridden(self):
        shop = ShopFactory()
        item = ItemFactory()
        InventoryFactory(shop=shop, item=item, quantity=10)
        transaction = TransactionFactory(shop=shop)

        self.selenium.get(self.live_server_url + "/admin/market/itemsale/add/")
        # Should ask for confirmation of change
        self.assertIn(CONFIRM_ADD, self.selenium.page_source)

        self.set_value(by=By.NAME, by_value="quantity", value="9")
        self.set_value(by=By.NAME, by_value="total", value="10.00")
        self.set_value(by=By.NAME, by_value="currency", value="USD")
        self.select_index(by=By.NAME, by_value="transaction", index=1)
        self.select_index(by=By.NAME, by_value="item", index=1)

        self.selenium.find_element(By.NAME, "_continue").click()

        # Should not have been added yet
        self.assertEqual(ItemSale.objects.count(), 0)

        # Should have hidden form containing the updated currency
        self.assertIn("Confirm", self.selenium.page_source)
        hidden_form = self.selenium.find_element(By.ID, "hidden-form")
        currency = hidden_form.find_element(By.NAME, "currency")
        self.assertIn("USD", currency.get_attribute("value"))

        # Confirm the change
        self.selenium.find_element(By.NAME, "_continue").click()

        # Should persist change
        self.assertEqual(ItemSale.objects.count(), 1)

        # Ensure that the date and timestamp saved correctly
        item_sale = ItemSale.objects.first()
        self.assertEqual(item_sale.transaction, transaction)
        self.assertEqual(item_sale.item, item)
        self.assertEqual(item_sale.currency, "USD")

    def test_cannot_confirm_for_models_with_clean_overridden_if_clean_fails(self):
        shop = ShopFactory()
        item = ItemFactory()
        InventoryFactory(shop=shop, item=item, quantity=1)
        TransactionFactory(shop=shop)

        self.selenium.get(self.live_server_url + "/admin/market/itemsale/add/")
        # Should ask for confirmation of change
        self.assertIn(CONFIRM_ADD, self.selenium.page_source)

        self.set_value(by=By.NAME, by_value="quantity", value="9")
        self.set_value(by=By.NAME, by_value="total", value="10.00")
        self.set_value(by=By.NAME, by_value="currency", value="USD")
        self.select_index(by=By.NAME, by_value="transaction", index=1)
        self.select_index(by=By.NAME, by_value="item", index=1)

        self.selenium.find_element(By.NAME, "_continue").click()

        # Should show errors and not confirmation page
        self.assertNotIn("Confirm", self.selenium.page_source)
        self.assertIn("Shop does not have enough of the item stocked", self.selenium.page_source)
        self.assertIn(CONFIRM_ADD, self.selenium.page_source)

        # Should not have been added yet
        self.assertEqual(ItemSale.objects.count(), 0)

        # Now fix the issue
        self.set_value(by=By.NAME, by_value="quantity", value="1")
        self.selenium.find_element(By.NAME, "_continue").click()

        # Should have hidden form containing the updated currency
        self.assertIn("Confirm", self.selenium.page_source)
        hidden_form = self.selenium.find_element(By.ID, "hidden-form")
        quantity = hidden_form.find_element(By.NAME, "quantity")
        self.assertIn("1", str(quantity.get_attribute("value")))

        # Confirm change
        self.selenium.find_element(By.NAME, "_continue").click()

        # Should persist change
        self.assertEqual(ItemSale.objects.count(), 1)
