"""
Test our StatsView in ivatar.views
"""

import json
import os
import django
from django.test import TestCase
from django.test import Client
from django.contrib.auth.models import User
from ivatar.utils import random_string, random_ip_address

os.environ["DJANGO_SETTINGS_MODULE"] = "ivatar.settings"
django.setup()


class StatsTester(TestCase):
    """
    Test class for StatsView
    """

    client = Client()
    user = None
    username = random_string()
    password = random_string()

    def login(self):
        """
        Login as user
        """
        self.client.login(username=self.username, password=self.password)

    def setUp(self):
        """
        Prepare for tests.
        - Create user
        """
        self.user = User.objects.create_user(
            username=self.username,
            password=self.password,
        )

    def test_stats_basic(self):
        """
        Test basic stats functionality
        """
        response = self.client.get("/stats/", follow=True)
        self.assertEqual(response.status_code, 200, "unable to fetch stats!")
        j = json.loads(response.content)
        self.assertEqual(j["users"], 1, "user count incorrect")
        self.assertEqual(j["mails"], 0, "mails count incorrect")
        self.assertEqual(j["openids"], 0, "openids count incorrect")
        self.assertEqual(j["unconfirmed_mails"], 0, "unconfirmed mails count incorrect")
        self.assertEqual(
            j["unconfirmed_openids"], 0, "unconfirmed openids count incorrect"
        )
        self.assertEqual(j["avatars"], 0, "avatars count incorrect")

    def test_stats_comprehensive(self):
        """
        Test comprehensive stats with actual data
        """
        from ivatar.ivataraccount.models import (
            ConfirmedEmail,
            ConfirmedOpenId,
            Photo,
            UnconfirmedEmail,
            UnconfirmedOpenId,
        )

        # Create test data with random values
        email1 = ConfirmedEmail.objects.create(
            user=self.user,
            email=f"{random_string()}@{random_string()}.{random_string(2)}",
            ip_address=random_ip_address(),
        )
        email1.access_count = 100
        email1.save()

        email2 = ConfirmedEmail.objects.create(
            user=self.user,
            email=f"{random_string()}@{random_string()}.{random_string(2)}",
            ip_address=random_ip_address(),
        )
        email2.access_count = 50
        email2.save()

        openid1 = ConfirmedOpenId.objects.create(
            user=self.user,
            openid=f"http://{random_string()}.{random_string()}.org/",
            ip_address=random_ip_address(),
        )
        openid1.access_count = 75
        openid1.save()

        # Create photos with valid image data (minimal PNG)
        # PNG header + minimal data
        png_data = b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\nIDATx\x9cc```\x00\x00\x00\x04\x00\x01\xdd\x8d\xb4\x1c\x00\x00\x00\x00IEND\xaeB`\x82"

        photo1 = Photo.objects.create(
            user=self.user, data=png_data, format="png", ip_address=random_ip_address()
        )
        photo1.access_count = 200
        photo1.save()

        photo2 = Photo.objects.create(
            user=self.user,
            data=png_data,  # Same data for testing
            format="png",  # Same format for testing
            ip_address=random_ip_address(),
        )
        photo2.access_count = 150
        photo2.save()

        # Associate photos with emails/openids
        email1.photo = photo1
        email1.save()
        email2.photo = photo2
        email2.save()
        openid1.photo = photo1
        openid1.save()

        # Create unconfirmed entries
        UnconfirmedEmail.objects.create(
            user=self.user,
            email=f"{random_string()}@{random_string()}.{random_string(2)}",
            ip_address=random_ip_address(),
        )

        UnconfirmedOpenId.objects.create(
            user=self.user,
            openid=f"http://{random_string()}.{random_string()}.org/",
            ip_address=random_ip_address(),
        )

        # Test the stats endpoint
        response = self.client.get("/stats/")
        self.assertEqual(response.status_code, 200, "unable to fetch stats!")
        j = json.loads(response.content)

        # Test basic counts
        self.assertEqual(j["users"], 1, "user count incorrect")
        self.assertEqual(j["mails"], 2, "mails count incorrect")
        self.assertEqual(j["openids"], 1, "openids count incorrect")
        self.assertEqual(j["unconfirmed_mails"], 1, "unconfirmed mails count incorrect")
        self.assertEqual(
            j["unconfirmed_openids"], 1, "unconfirmed openids count incorrect"
        )
        self.assertEqual(j["avatars"], 2, "avatars count incorrect")

        # Test top viewed avatars
        self.assertIn("top_viewed_avatars", j, "top_viewed_avatars missing")
        self.assertEqual(
            len(j["top_viewed_avatars"]), 2, "should have 2 top viewed avatars"
        )
        # The top viewed avatar should be the one with highest associated email/openid access count
        self.assertEqual(
            j["top_viewed_avatars"][0]["access_count"],
            100,
            "top avatar access count incorrect",
        )
        # Check that avatar_url is present and starts with the correct base URL
        self.assertIn("avatar_url", j["top_viewed_avatars"][0], "avatar_url missing")
        self.assertTrue(
            j["top_viewed_avatars"][0]["avatar_url"].startswith(
                "https://libravatar.org/avatar/"
            ),
            "avatar_url should start with https://libravatar.org/avatar/",
        )

        # Check that avatar_url is present and starts with the correct base URL
        self.assertIn("avatar_url", j["top_queried_emails"][0], "avatar_url missing")
        self.assertTrue(
            j["top_queried_emails"][0]["avatar_url"].startswith(
                "https://libravatar.org/avatar/"
            ),
            "avatar_url should start with https://libravatar.org/avatar/",
        )

        # Check that avatar_url is present and starts with the correct base URL
        self.assertIn("avatar_url", j["top_queried_openids"][0], "avatar_url missing")
        self.assertTrue(
            j["top_queried_openids"][0]["avatar_url"].startswith(
                "https://libravatar.org/avatar/"
            ),
            "avatar_url should start with https://libravatar.org/avatar/",
        )

        # Test photo format distribution
        self.assertIn(
            "photo_format_distribution", j, "photo_format_distribution missing"
        )
        formats = {
            item["format"]: item["count"] for item in j["photo_format_distribution"]
        }
        self.assertEqual(formats["png"], 2, "png format count incorrect")

        # Test user activity stats
        self.assertIn("user_activity", j, "user_activity missing")
        self.assertEqual(
            j["user_activity"]["users_with_multiple_photos"],
            1,
            "users with multiple photos incorrect",
        )
        self.assertEqual(
            j["user_activity"]["users_with_both_email_and_openid"],
            1,
            "users with both email and openid incorrect",
        )
        self.assertEqual(
            j["user_activity"]["average_photos_per_user"],
            2.0,
            "average photos per user incorrect",
        )

        # Test Bluesky handles (should be empty)
        self.assertIn("bluesky_handles", j, "bluesky_handles missing")
        self.assertEqual(
            j["bluesky_handles"]["total_bluesky_handles"],
            0,
            "total bluesky handles should be 0",
        )

        # Test photo size stats
        self.assertIn("photo_size_stats", j, "photo_size_stats missing")
        self.assertGreater(
            j["photo_size_stats"]["average_size_bytes"],
            0,
            "average photo size should be > 0",
        )
        self.assertEqual(
            j["photo_size_stats"]["total_photos_analyzed"],
            2,
            "total photos analyzed incorrect",
        )

        # Test potential duplicate photos
        self.assertIn(
            "potential_duplicate_photos", j, "potential_duplicate_photos missing"
        )
        self.assertEqual(
            j["potential_duplicate_photos"]["potential_duplicate_groups"],
            1,
            "should have 1 duplicate group (same PNG data)",
        )

    def test_stats_edge_cases(self):
        """
        Test edge cases for stats
        """
        # Test with no data
        response = self.client.get("/stats/")
        self.assertEqual(response.status_code, 200, "unable to fetch stats!")
        j = json.loads(response.content)

        # All lists should be empty
        self.assertEqual(
            len(j["top_viewed_avatars"]), 0, "top_viewed_avatars should be empty"
        )
        self.assertEqual(
            len(j["top_queried_emails"]), 0, "top_queried_emails should be empty"
        )
        self.assertEqual(
            len(j["top_queried_openids"]), 0, "top_queried_openids should be empty"
        )
        self.assertEqual(
            len(j["photo_format_distribution"]),
            0,
            "photo_format_distribution should be empty",
        )
        self.assertEqual(
            j["bluesky_handles"]["total_bluesky_handles"],
            0,
            "bluesky_handles should be 0",
        )
        self.assertEqual(
            j["photo_size_stats"]["total_photos_analyzed"],
            0,
            "photo_size_stats should be 0",
        )
        self.assertEqual(
            j["potential_duplicate_photos"]["potential_duplicate_groups"],
            0,
            "potential_duplicate_photos should be 0",
        )

    def test_stats_with_bluesky_handles(self):
        """
        Test stats with Bluesky handles
        """
        from ivatar.ivataraccount.models import ConfirmedEmail, ConfirmedOpenId

        # Create email with Bluesky handle
        email = ConfirmedEmail.objects.create(
            user=self.user,
            email=f"{random_string()}@{random_string()}.{random_string(2)}",
            ip_address=random_ip_address(),
        )
        email.bluesky_handle = f"{random_string()}.bsky.social"
        email.access_count = 100
        email.save()

        # Create OpenID with Bluesky handle
        openid = ConfirmedOpenId.objects.create(
            user=self.user,
            openid=f"http://{random_string()}.{random_string()}.org/",
            ip_address=random_ip_address(),
        )
        openid.bluesky_handle = f"{random_string()}.bsky.social"
        openid.access_count = 50
        openid.save()

        response = self.client.get("/stats/")
        self.assertEqual(response.status_code, 200, "unable to fetch stats!")
        j = json.loads(response.content)

        # Test Bluesky handles stats
        self.assertEqual(
            j["bluesky_handles"]["total_bluesky_handles"],
            2,
            "total bluesky handles incorrect",
        )
        self.assertEqual(
            j["bluesky_handles"]["bluesky_emails"], 1, "bluesky emails count incorrect"
        )
        self.assertEqual(
            j["bluesky_handles"]["bluesky_openids"],
            1,
            "bluesky openids count incorrect",
        )

    def test_stats_photo_duplicates(self):
        """
        Test potential duplicate photos detection
        """
        from ivatar.ivataraccount.models import Photo

        # Create photos with same format and size (potential duplicates)
        # PNG header + minimal data
        png_data = b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\nIDATx\x9cc```\x00\x00\x00\x04\x00\x01\xdd\x8d\xb4\x1c\x00\x00\x00\x00IEND\xaeB`\x82"

        Photo.objects.create(
            user=self.user, data=png_data, format="png", ip_address=random_ip_address()
        )
        Photo.objects.create(
            user=self.user,
            data=png_data,  # Same size
            format="png",  # Same format
            ip_address=random_ip_address(),
        )
        Photo.objects.create(
            user=self.user,
            data=png_data,  # Same size but different format
            format="png",  # Same format for testing
            ip_address=random_ip_address(),
        )

        response = self.client.get("/stats/")
        self.assertEqual(response.status_code, 200, "unable to fetch stats!")
        j = json.loads(response.content)

        # Should detect potential duplicates
        self.assertEqual(
            j["potential_duplicate_photos"]["potential_duplicate_groups"],
            1,
            "should have 1 duplicate group",
        )
        self.assertEqual(
            j["potential_duplicate_photos"]["total_potential_duplicate_photos"],
            3,
            "should have 3 potential duplicate photos",
        )
        self.assertEqual(
            len(j["potential_duplicate_photos"]["potential_duplicate_groups_detail"]),
            1,
            "should have 1 duplicate group detail",
        )
