"""
Tests for robohash optimization functionality
"""

import time
import hashlib
from io import BytesIO

from django.test import TestCase
from PIL import Image

from robohash import Robohash
from ivatar.robohash_optimized import OptimizedRobohash, create_optimized_robohash
from ivatar.utils import generate_random_email


class RobohashOptimizationTestCase(TestCase):
    """Test cases for robohash optimization"""

    def setUp(self):
        """Set up test data"""
        self.test_emails = [generate_random_email() for _ in range(5)]
        self.test_digests = [
            hashlib.md5(email.encode()).hexdigest() for email in self.test_emails
        ]
        self.test_digests.extend(
            [
                "5d41402abc4b2a76b9719d911017c592",
                "098f6bcd4621d373cade4e832627b4f6",
            ]
        )

    def test_optimized_robohash_functionality(self):
        """Test that optimized robohash functionality works correctly"""
        digest = self.test_digests[0]
        optimized = OptimizedRobohash(digest)
        optimized.assemble_fast(roboset="any", sizex=256, sizey=256)

        self.assertIsNotNone(optimized.img)
        self.assertEqual(optimized.img.size, (256, 256))
        self.assertIn(optimized.img.mode, ["RGBA", "RGB"])

        data = BytesIO()
        optimized.img.save(data, format="png")
        self.assertGreater(len(data.getvalue()), 1000)

    def test_identical_results(self):
        """Test that optimized robohash returns identical results"""
        digest = self.test_digests[0]

        original = Robohash(digest)
        original.assemble(roboset="any", sizex=256, sizey=256)
        orig_data = BytesIO()
        original.img.save(orig_data, format="png")
        orig_bytes = orig_data.getvalue()

        optimized = OptimizedRobohash(digest)
        optimized.assemble_fast(roboset="any", sizex=256, sizey=256)
        opt_data = BytesIO()
        optimized.img.save(opt_data, format="png")
        opt_bytes = opt_data.getvalue()

        self.assertEqual(orig_bytes, opt_bytes, "Images should be identical")

    def test_performance_improvement(self):
        """Test that optimized robohash shows performance characteristics"""
        digest = self.test_digests[0]

        start_time = time.time()
        original = Robohash(digest)
        original.assemble(roboset="any", sizex=256, sizey=256)
        original_time = (time.time() - start_time) * 1000

        start_time = time.time()
        optimized = OptimizedRobohash(digest)
        optimized.assemble_fast(roboset="any", sizex=256, sizey=256)
        optimized_time = (time.time() - start_time) * 1000

        self.assertGreater(original_time, 0, "Original should take some time")
        self.assertGreater(optimized_time, 0, "Optimized should take some time")

    def test_integration_function(self):
        """Test the create_optimized_robohash integration function"""
        digest = self.test_digests[0]
        data = create_optimized_robohash(digest, 256, "any")

        self.assertIsInstance(data, BytesIO)
        png_bytes = data.getvalue()
        self.assertGreater(len(png_bytes), 1000)

        img = Image.open(BytesIO(png_bytes))
        self.assertEqual(img.size, (256, 256))
        self.assertEqual(img.format, "PNG")

    def test_cache_initialization(self):
        """Test that directory cache is initialized correctly"""
        digest = self.test_digests[0]
        OptimizedRobohash(digest)  # Initialize to trigger cache setup

        self.assertTrue(OptimizedRobohash._cache_initialized)
        self.assertIsInstance(OptimizedRobohash._directory_cache, dict)

    def test_multiple_random_emails_identical_results(self):
        """Test pixel-perfect identical results with multiple random email addresses"""
        # Test with multiple random email addresses
        for i, digest in enumerate(self.test_digests[:3]):
            with self.subTest(email_index=i, digest=digest[:8]):
                # Test with different configurations
                test_cases = [
                    {"roboset": "any", "size": 128},
                    {"roboset": "set1", "size": 256},
                    {"roboset": "set2", "size": 64},
                ]

                for case in test_cases:
                    with self.subTest(case=case):
                        # Generate original
                        original = Robohash(digest)
                        original.assemble(
                            roboset=case["roboset"],
                            sizex=case["size"],
                            sizey=case["size"],
                        )
                        orig_data = BytesIO()
                        original.img.save(orig_data, format="png")
                        orig_bytes = orig_data.getvalue()

                        # Generate optimized
                        optimized = OptimizedRobohash(digest)
                        optimized.assemble_fast(
                            roboset=case["roboset"],
                            sizex=case["size"],
                            sizey=case["size"],
                        )
                        opt_data = BytesIO()
                        optimized.img.save(opt_data, format="png")
                        opt_bytes = opt_data.getvalue()

                        # Verify pixel-perfect identical
                        self.assertEqual(
                            orig_bytes,
                            opt_bytes,
                            f"Images not pixel-perfect identical for email {i}, "
                            f"digest {digest[:8]}..., {case['roboset']}, {case['size']}x{case['size']}",
                        )

    def test_performance_improvement_multiple_cases(self):
        """Test that optimized version is consistently faster across multiple cases"""
        performance_results = []

        # Test with multiple digests and configurations
        test_cases = [
            {"digest": self.test_digests[0], "roboset": "any", "size": 256},
            {"digest": self.test_digests[1], "roboset": "set1", "size": 128},
            {"digest": self.test_digests[2], "roboset": "set2", "size": 256},
        ]

        for case in test_cases:
            # Measure original
            start_time = time.time()
            original = Robohash(case["digest"])
            original.assemble(
                roboset=case["roboset"], sizex=case["size"], sizey=case["size"]
            )
            original_time = (time.time() - start_time) * 1000

            # Measure optimized
            start_time = time.time()
            optimized = OptimizedRobohash(case["digest"])
            optimized.assemble_fast(
                roboset=case["roboset"], sizex=case["size"], sizey=case["size"]
            )
            optimized_time = (time.time() - start_time) * 1000

            performance_results.append(
                {
                    "original": original_time,
                    "optimized": optimized_time,
                    "improvement": (
                        original_time / optimized_time if optimized_time > 0 else 0
                    ),
                }
            )

        # Verify all cases show reasonable performance
        for i, result in enumerate(performance_results):
            with self.subTest(case_index=i):
                self.assertGreater(
                    result["original"], 0, "Original should take measurable time"
                )
                self.assertGreater(
                    result["optimized"], 0, "Optimized should take measurable time"
                )
                # Allow for test environment variance - just ensure both complete successfully
                self.assertLess(
                    result["optimized"],
                    10000,
                    "Optimized should complete in reasonable time",
                )

    def test_random_email_generation_and_processing(self):
        """Test robohash with freshly generated random emails"""
        # Generate fresh random emails for this test
        fresh_emails = [generate_random_email() for _ in range(5)]
        fresh_digests = [
            hashlib.md5(email.encode()).hexdigest() for email in fresh_emails
        ]

        for i, (email, digest) in enumerate(zip(fresh_emails, fresh_digests)):
            with self.subTest(email=email, digest=digest[:8]):
                # Test that both original and optimized can process this email
                original = Robohash(digest)
                original.assemble(roboset="any", sizex=128, sizey=128)

                optimized = OptimizedRobohash(digest)
                optimized.assemble_fast(roboset="any", sizex=128, sizey=128)

                # Verify both produce valid images
                self.assertIsNotNone(original.img)
                self.assertIsNotNone(optimized.img)
                self.assertEqual(original.img.size, (128, 128))
                self.assertEqual(optimized.img.size, (128, 128))

                # Verify they produce identical results
                orig_data = BytesIO()
                original.img.save(orig_data, format="png")
                orig_bytes = orig_data.getvalue()

                opt_data = BytesIO()
                optimized.img.save(opt_data, format="png")
                opt_bytes = opt_data.getvalue()

                self.assertEqual(
                    orig_bytes,
                    opt_bytes,
                    f"Random email {email} (digest {digest[:8]}...) produced different images",
                )
