"""
Tests confirmation of add/change
on ModelAdmin that utilize caches
and S3 as a storage backend
"""

import os
from importlib import reload

import localstack_client.session
import pkg_resources
import pytest
from django.conf import settings
from django.core.files.uploadedfile import SimpleUploadedFile
from selenium.webdriver.common.by import By
from selenium.webdriver.remote.file_detector import LocalFileDetector

from admin_action_tools.constants import CONFIRM_CHANGE
from admin_action_tools.tests.helpers import AdminConfirmIntegrationTestCase
from tests.market.admin import shoppingmall_admin
from tests.market.models import Item


class ConfirmWithS3StorageTests(AdminConfirmIntegrationTestCase):
    def setUp(self):
        self.selenium.file_detector = LocalFileDetector()
        session = localstack_client.session.Session(region_name="us-west-1")
        self.s3 = session.resource("s3")
        self.bucket = self.s3.Bucket(settings.AWS_STORAGE_BUCKET_NAME)
        # Delete all current files
        for obj in self.get_all_media():
            obj.delete()
        super().setUp()

    def get_all_media(self):
        return list(self.bucket.objects.filter(Prefix="media/"))

    def tearDown(self):
        reload(shoppingmall_admin)
        # Delete all current files
        for obj in self.get_all_media():
            obj.delete()
        super().tearDown()

    def test_s3_is_being_used(self):
        self.assertTrue(settings.USE_S3)
        self.assertIsNotNone(settings.AWS_ACCESS_KEY_ID)
        self.assertEqual(
            settings.STORAGES["default"]["BACKEND"],
            "tests.storage_backends.PublicMediaStorage",
        )

    def test_should_save_file_additions(self):
        selenium_version = pkg_resources.get_distribution("selenium").parsed_version
        if selenium_version.major < 4:
            pytest.skip("Known issue `https://github.com/SeleniumHQ/selenium/issues/8762` with this selenium version.")

        item = Item.objects.create(name="item", price=1, currency=Item.VALID_CURRENCIES[0][0])

        self.selenium.get(self.live_server_url + f"/admin/market/item/{item.id}/change/")
        self.assertIn(CONFIRM_CHANGE, self.selenium.page_source)

        # Make a change to trigger confirmation page
        price = self.selenium.find_element(By.NAME, "price")
        price.send_keys(2)

        # Upload a new file
        self.selenium.find_element(By.ID, "id_file").send_keys(
            os.getcwd() + "/admin_action_tools/tests/snapshot/screenshot.png"
        )

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

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

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

        item.refresh_from_db()
        self.assertRegex(item.file.name, r"screenshot.*\.png$")
        self.assertEqual(21, int(item.price))

        # Check S3 for the file
        objects = self.get_all_media()
        self.assertEqual(len(objects), 1)
        self.assertRegex(objects[0].key, r"screenshot.*\.png$")

    def test_should_save_file_changes(self):
        selenium_version = pkg_resources.get_distribution("selenium").parsed_version
        if selenium_version.major < 4:
            pytest.skip("Known issue `https://github.com/SeleniumHQ/selenium/issues/8762` with this selenium version.")

        file = SimpleUploadedFile(
            name="old_file.jpg",
            content=open("admin_action_tools/tests/snapshot/screenshot.png", "rb").read(),
            content_type="image/jpeg",
        )
        item = Item.objects.create(name="item", price=1, currency=Item.VALID_CURRENCIES[0][0], file=file)

        self.selenium.get(self.live_server_url + f"/admin/market/item/{item.id}/change/")
        self.assertIn(CONFIRM_CHANGE, self.selenium.page_source)

        # Make a change to trigger confirmation page
        price = self.selenium.find_element(By.NAME, "price")
        price.send_keys(2)

        # Upload a new file
        self.selenium.find_element(By.ID, "id_file").send_keys(
            os.getcwd() + "/admin_action_tools/tests/snapshot/screenshot.png"
        )

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

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

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

        item.refresh_from_db()
        self.assertEqual(21, int(item.price))
        self.assertRegex(item.file.name, r"screenshot.*\.png$")

        # Check S3 for the file
        objects = self.get_all_media()
        self.assertEqual(len(objects), 2)
        get_last_modified = lambda obj: int(obj.last_modified.strftime("%s"))
        objects_by_last_modified = [obj for obj in sorted(objects, key=get_last_modified)]
        self.assertRegex(objects_by_last_modified[-1].key, r"screenshot.*\.png$")
        self.assertRegex(objects_by_last_modified[0].key, r"old_file.*\.jpg$")

    def test_should_remove_file_if_clear_selected(self):
        file = SimpleUploadedFile(
            name="old_file.jpg",
            content=open("admin_action_tools/tests/snapshot/screenshot.png", "rb").read(),
            content_type="image/jpeg",
        )
        item = Item.objects.create(name="item", price=1, currency=Item.VALID_CURRENCIES[0][0], file=file)

        self.selenium.get(self.live_server_url + f"/admin/market/item/{item.id}/change/")
        self.assertIn(CONFIRM_CHANGE, self.selenium.page_source)

        # Make a change to trigger confirmation page
        price = self.selenium.find_element(By.NAME, "price")
        price.send_keys(2)

        # Choose to clear the existing file
        self.selenium.find_element(By.ID, "file-clear_id").click()
        self.assertTrue(self.selenium.find_element(By.XPATH, ".//*[@id='file-clear_id']").get_attribute("checked"))

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

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

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

        item.refresh_from_db()
        self.assertEqual(21, int(item.price))
        # Should have cleared `file` since clear was selected
        self.assertFalse(item.file)

        # Check S3 for the file
        # Since deleting from model instance in Django does not automatically
        #   delete from storage, the old file should still be in S3
        objects = self.get_all_media()
        self.assertEqual(len(objects), 1)
        self.assertRegex(objects[0].key, r"old_file.*\.jpg$")
