"""
Tests for ParkMediaService.

Following Django styleguide pattern: test__<context>__<action>__<expected_outcome>
"""

from unittest.mock import Mock, patch

import pytest
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase

from apps.parks.models import ParkPhoto
from apps.parks.services.media_service import ParkMediaService
from tests.factories import (
    ParkFactory,
    ParkPhotoFactory,
    StaffUserFactory,
    UserFactory,
)


@pytest.mark.django_db
class TestParkMediaServiceUploadPhoto(TestCase):
    """Tests for ParkMediaService.upload_photo."""

    @patch("apps.parks.services.media_service.MediaService.validate_image_file")
    @patch("apps.parks.services.media_service.MediaService.process_image")
    @patch("apps.parks.services.media_service.MediaService.generate_default_caption")
    @patch("apps.parks.services.media_service.MediaService.extract_exif_date")
    def test__upload_photo__valid_image__creates_photo(
        self,
        mock_exif,
        mock_caption,
        mock_process,
        mock_validate,
    ):
        """Test upload_photo creates photo with valid image."""
        mock_validate.return_value = (True, None)
        mock_process.return_value = Mock()
        mock_caption.return_value = "Photo by testuser"
        mock_exif.return_value = None

        park = ParkFactory()
        user = UserFactory()
        image_file = SimpleUploadedFile("test.jpg", b"fake image content", content_type="image/jpeg")

        photo = ParkMediaService.upload_photo(
            park=park,
            image_file=image_file,
            user=user,
            caption="Test caption",
            alt_text="Test alt",
            is_primary=False,
            auto_approve=True,
        )

        assert photo.park == park
        assert photo.caption == "Test caption"
        assert photo.alt_text == "Test alt"
        assert photo.uploaded_by == user
        assert photo.is_approved is True

    @patch("apps.parks.services.media_service.MediaService.validate_image_file")
    def test__upload_photo__invalid_image__raises_value_error(self, mock_validate):
        """Test upload_photo raises ValueError for invalid image."""
        mock_validate.return_value = (False, "Invalid file type")

        park = ParkFactory()
        user = UserFactory()
        image_file = SimpleUploadedFile("test.txt", b"not an image", content_type="text/plain")

        with pytest.raises(ValueError) as exc_info:
            ParkMediaService.upload_photo(
                park=park,
                image_file=image_file,
                user=user,
            )

        assert "Invalid file type" in str(exc_info.value)


@pytest.mark.django_db
class TestParkMediaServiceGetParkPhotos(TestCase):
    """Tests for ParkMediaService.get_park_photos."""

    def test__get_park_photos__approved_only_true__filters_approved(self):
        """Test get_park_photos with approved_only filters unapproved photos."""
        park = ParkFactory()
        approved = ParkPhotoFactory(park=park, is_approved=True)
        unapproved = ParkPhotoFactory(park=park, is_approved=False)

        result = ParkMediaService.get_park_photos(park, approved_only=True)

        assert approved in result
        assert unapproved not in result

    def test__get_park_photos__approved_only_false__returns_all(self):
        """Test get_park_photos with approved_only=False returns all photos."""
        park = ParkFactory()
        approved = ParkPhotoFactory(park=park, is_approved=True)
        unapproved = ParkPhotoFactory(park=park, is_approved=False)

        result = ParkMediaService.get_park_photos(park, approved_only=False)

        assert approved in result
        assert unapproved in result

    def test__get_park_photos__primary_first__orders_primary_first(self):
        """Test get_park_photos with primary_first orders primary photos first."""
        park = ParkFactory()
        ParkPhotoFactory(park=park, is_primary=False, is_approved=True)
        primary = ParkPhotoFactory(park=park, is_primary=True, is_approved=True)

        result = ParkMediaService.get_park_photos(park, primary_first=True)

        # Primary should be first
        assert result[0] == primary


@pytest.mark.django_db
class TestParkMediaServiceGetPrimaryPhoto(TestCase):
    """Tests for ParkMediaService.get_primary_photo."""

    def test__get_primary_photo__has_primary__returns_primary(self):
        """Test get_primary_photo returns primary photo when exists."""
        park = ParkFactory()
        primary = ParkPhotoFactory(park=park, is_primary=True, is_approved=True)
        ParkPhotoFactory(park=park, is_primary=False, is_approved=True)

        result = ParkMediaService.get_primary_photo(park)

        assert result == primary

    def test__get_primary_photo__no_primary__returns_none(self):
        """Test get_primary_photo returns None when no primary exists."""
        park = ParkFactory()
        ParkPhotoFactory(park=park, is_primary=False, is_approved=True)

        result = ParkMediaService.get_primary_photo(park)

        assert result is None

    def test__get_primary_photo__unapproved_primary__returns_none(self):
        """Test get_primary_photo ignores unapproved primary photos."""
        park = ParkFactory()
        ParkPhotoFactory(park=park, is_primary=True, is_approved=False)

        result = ParkMediaService.get_primary_photo(park)

        assert result is None


@pytest.mark.django_db
class TestParkMediaServiceSetPrimaryPhoto(TestCase):
    """Tests for ParkMediaService.set_primary_photo."""

    def test__set_primary_photo__valid_photo__sets_as_primary(self):
        """Test set_primary_photo sets photo as primary."""
        park = ParkFactory()
        photo = ParkPhotoFactory(park=park, is_primary=False)

        result = ParkMediaService.set_primary_photo(park, photo)

        photo.refresh_from_db()
        assert result is True
        assert photo.is_primary is True

    def test__set_primary_photo__unsets_existing_primary(self):
        """Test set_primary_photo unsets existing primary photo."""
        park = ParkFactory()
        old_primary = ParkPhotoFactory(park=park, is_primary=True)
        new_primary = ParkPhotoFactory(park=park, is_primary=False)

        ParkMediaService.set_primary_photo(park, new_primary)

        old_primary.refresh_from_db()
        new_primary.refresh_from_db()

        assert old_primary.is_primary is False
        assert new_primary.is_primary is True

    def test__set_primary_photo__wrong_park__returns_false(self):
        """Test set_primary_photo returns False for photo from different park."""
        park1 = ParkFactory()
        park2 = ParkFactory()
        photo = ParkPhotoFactory(park=park2)

        result = ParkMediaService.set_primary_photo(park1, photo)

        assert result is False


@pytest.mark.django_db
class TestParkMediaServiceApprovePhoto(TestCase):
    """Tests for ParkMediaService.approve_photo."""

    def test__approve_photo__unapproved_photo__approves_it(self):
        """Test approve_photo approves an unapproved photo."""
        photo = ParkPhotoFactory(is_approved=False)
        staff_user = StaffUserFactory()

        result = ParkMediaService.approve_photo(photo, staff_user)

        photo.refresh_from_db()
        assert result is True
        assert photo.is_approved is True


@pytest.mark.django_db
class TestParkMediaServiceDeletePhoto(TestCase):
    """Tests for ParkMediaService.delete_photo."""

    def test__delete_photo__valid_photo__deletes_it(self):
        """Test delete_photo deletes a photo."""
        photo = ParkPhotoFactory()
        photo_id = photo.pk
        staff_user = StaffUserFactory()

        result = ParkMediaService.delete_photo(photo, staff_user)

        assert result is True
        assert not ParkPhoto.objects.filter(pk=photo_id).exists()


@pytest.mark.django_db
class TestParkMediaServiceGetPhotoStats(TestCase):
    """Tests for ParkMediaService.get_photo_stats."""

    def test__get_photo_stats__returns_correct_counts(self):
        """Test get_photo_stats returns correct statistics."""
        park = ParkFactory()
        ParkPhotoFactory(park=park, is_approved=True)
        ParkPhotoFactory(park=park, is_approved=True)
        ParkPhotoFactory(park=park, is_approved=False)
        ParkPhotoFactory(park=park, is_approved=True, is_primary=True)

        stats = ParkMediaService.get_photo_stats(park)

        assert stats["total_photos"] == 4
        assert stats["approved_photos"] == 3
        assert stats["pending_photos"] == 1
        assert stats["has_primary"] is True

    def test__get_photo_stats__no_photos__returns_zeros(self):
        """Test get_photo_stats returns zeros when no photos."""
        park = ParkFactory()

        stats = ParkMediaService.get_photo_stats(park)

        assert stats["total_photos"] == 0
        assert stats["approved_photos"] == 0
        assert stats["pending_photos"] == 0
        assert stats["has_primary"] is False


@pytest.mark.django_db
class TestParkMediaServiceBulkApprovePhotos(TestCase):
    """Tests for ParkMediaService.bulk_approve_photos."""

    def test__bulk_approve_photos__multiple_photos__approves_all(self):
        """Test bulk_approve_photos approves multiple photos."""
        park = ParkFactory()
        photo1 = ParkPhotoFactory(park=park, is_approved=False)
        photo2 = ParkPhotoFactory(park=park, is_approved=False)
        photo3 = ParkPhotoFactory(park=park, is_approved=False)
        staff_user = StaffUserFactory()

        count = ParkMediaService.bulk_approve_photos([photo1, photo2, photo3], staff_user)

        assert count == 3
        photo1.refresh_from_db()
        photo2.refresh_from_db()
        photo3.refresh_from_db()
        assert photo1.is_approved is True
        assert photo2.is_approved is True
        assert photo3.is_approved is True

    def test__bulk_approve_photos__empty_list__returns_zero(self):
        """Test bulk_approve_photos with empty list returns 0."""
        staff_user = StaffUserFactory()

        count = ParkMediaService.bulk_approve_photos([], staff_user)

        assert count == 0
