"""Unit tests for NFO models."""

import pytest
from pydantic import ValidationError

from src.core.entities.nfo_models import (
    ActorInfo,
    ImageInfo,
    NamedSeason,
    RatingInfo,
    TVShowNFO,
    UniqueID,
)


class TestRatingInfo:
    """Test RatingInfo model."""
    
    def test_rating_info_with_all_fields(self):
        """Test creating RatingInfo with all fields."""
        rating = RatingInfo(
            name="themoviedb",
            value=8.5,
            votes=1234,
            max_rating=10,
            default=True
        )
        
        assert rating.name == "themoviedb"
        assert rating.value == 8.5
        assert rating.votes == 1234
        assert rating.max_rating == 10
        assert rating.default is True
    
    def test_rating_info_with_minimal_fields(self):
        """Test creating RatingInfo with only required fields."""
        rating = RatingInfo(name="imdb", value=7.2)
        
        assert rating.name == "imdb"
        assert rating.value == 7.2
        assert rating.votes is None
        assert rating.max_rating == 10  # default
        assert rating.default is False  # default
    
    def test_rating_info_negative_value_rejected(self):
        """Test that negative rating values are rejected."""
        with pytest.raises(ValidationError):
            RatingInfo(name="test", value=-1.0)
    
    def test_rating_info_excessive_value_rejected(self):
        """Test that rating values > 10 are rejected."""
        with pytest.raises(ValidationError):
            RatingInfo(name="test", value=11.0)
    
    def test_rating_info_negative_votes_rejected(self):
        """Test that negative vote counts are rejected."""
        with pytest.raises(ValidationError):
            RatingInfo(name="test", value=5.0, votes=-10)
    
    def test_rating_info_zero_values_accepted(self):
        """Test that zero values are accepted."""
        rating = RatingInfo(name="test", value=0.0, votes=0)
        assert rating.value == 0.0
        assert rating.votes == 0


class TestActorInfo:
    """Test ActorInfo model."""
    
    def test_actor_info_with_all_fields(self):
        """Test creating ActorInfo with all fields."""
        actor = ActorInfo(
            name="John Doe",
            role="Main Character",
            thumb="https://example.com/actor.jpg",
            profile="https://example.com/profile",
            tmdbid=12345
        )
        
        assert actor.name == "John Doe"
        assert actor.role == "Main Character"
        assert str(actor.thumb) == "https://example.com/actor.jpg"
        assert str(actor.profile) == "https://example.com/profile"
        assert actor.tmdbid == 12345
    
    def test_actor_info_with_minimal_fields(self):
        """Test creating ActorInfo with only name."""
        actor = ActorInfo(name="Jane Smith")
        
        assert actor.name == "Jane Smith"
        assert actor.role is None
        assert actor.thumb is None
        assert actor.profile is None
        assert actor.tmdbid is None
    
    def test_actor_info_invalid_url_rejected(self):
        """Test that invalid URLs are rejected."""
        with pytest.raises(ValidationError):
            ActorInfo(name="Test", thumb="not-a-url")
    
    def test_actor_info_http_url_accepted(self):
        """Test that HTTP URLs are accepted."""
        actor = ActorInfo(
            name="Test",
            thumb="http://example.com/image.jpg"
        )
        assert str(actor.thumb) == "http://example.com/image.jpg"


class TestImageInfo:
    """Test ImageInfo model."""
    
    def test_image_info_with_all_fields(self):
        """Test creating ImageInfo with all fields."""
        image = ImageInfo(
            url="https://image.tmdb.org/t/p/w500/poster.jpg",
            aspect="poster",
            season=1,
            type="season"
        )
        
        assert str(image.url) == "https://image.tmdb.org/t/p/w500/poster.jpg"
        assert image.aspect == "poster"
        assert image.season == 1
        assert image.type == "season"
    
    def test_image_info_with_minimal_fields(self):
        """Test creating ImageInfo with only URL."""
        image = ImageInfo(url="https://example.com/image.jpg")
        
        assert str(image.url) == "https://example.com/image.jpg"
        assert image.aspect is None
        assert image.season is None
        assert image.type is None
    
    def test_image_info_invalid_url_rejected(self):
        """Test that invalid URLs are rejected."""
        with pytest.raises(ValidationError):
            ImageInfo(url="invalid-url")
    
    def test_image_info_negative_season_rejected(self):
        """Test that season < -1 is rejected."""
        with pytest.raises(ValidationError):
            ImageInfo(
                url="https://example.com/image.jpg",
                season=-2
            )
    
    def test_image_info_season_minus_one_accepted(self):
        """Test that season -1 is accepted (all seasons)."""
        image = ImageInfo(
            url="https://example.com/image.jpg",
            season=-1
        )
        assert image.season == -1


class TestNamedSeason:
    """Test NamedSeason model."""
    
    def test_named_season_creation(self):
        """Test creating NamedSeason."""
        season = NamedSeason(number=1, name="Season One")
        
        assert season.number == 1
        assert season.name == "Season One"
    
    def test_named_season_negative_number_rejected(self):
        """Test that negative season numbers are rejected."""
        with pytest.raises(ValidationError):
            NamedSeason(number=-1, name="Invalid")
    
    def test_named_season_zero_accepted(self):
        """Test that season 0 (specials) is accepted."""
        season = NamedSeason(number=0, name="Specials")
        assert season.number == 0


class TestUniqueID:
    """Test UniqueID model."""
    
    def test_unique_id_creation(self):
        """Test creating UniqueID."""
        uid = UniqueID(type="tmdb", value="12345", default=True)
        
        assert uid.type == "tmdb"
        assert uid.value == "12345"
        assert uid.default is True
    
    def test_unique_id_default_false(self):
        """Test UniqueID with default=False."""
        uid = UniqueID(type="imdb", value="tt1234567")
        assert uid.default is False


class TestTVShowNFO:
    """Test TVShowNFO model."""
    
    def test_tvshow_nfo_minimal_creation(self):
        """Test creating TVShowNFO with only required fields."""
        nfo = TVShowNFO(title="Test Show")
        
        assert nfo.title == "Test Show"
        assert nfo.showtitle == "Test Show"  # auto-set
        assert nfo.originaltitle == "Test Show"  # auto-set
        assert nfo.year is None
        assert nfo.studio == []
        assert nfo.genre == []
        assert nfo.watched is False
    
    def test_tvshow_nfo_with_all_basic_fields(self):
        """Test creating TVShowNFO with all basic fields."""
        nfo = TVShowNFO(
            title="Attack on Titan",
            originaltitle="Shingeki no Kyojin",
            showtitle="Attack on Titan",
            sorttitle="Attack on Titan",
            year=2013,
            plot="Humanity lives in fear of Titans.",
            outline="Titans attack humanity.",
            tagline="The world is cruel.",
            runtime=24,
            mpaa="TV-14",
            certification="14+",
            premiered="2013-04-07",
            status="Ended"
        )
        
        assert nfo.title == "Attack on Titan"
        assert nfo.originaltitle == "Shingeki no Kyojin"
        assert nfo.year == 2013
        assert nfo.plot == "Humanity lives in fear of Titans."
        assert nfo.runtime == 24
        assert nfo.premiered == "2013-04-07"
    
    def test_tvshow_nfo_empty_title_rejected(self):
        """Test that empty title is rejected."""
        with pytest.raises(ValidationError):
            TVShowNFO(title="")
    
    def test_tvshow_nfo_invalid_year_rejected(self):
        """Test that invalid years are rejected."""
        with pytest.raises(ValidationError):
            TVShowNFO(title="Test", year=1800)
        
        with pytest.raises(ValidationError):
            TVShowNFO(title="Test", year=2200)
    
    def test_tvshow_nfo_negative_runtime_rejected(self):
        """Test that negative runtime is rejected."""
        with pytest.raises(ValidationError):
            TVShowNFO(title="Test", runtime=-10)
    
    def test_tvshow_nfo_with_multi_value_fields(self):
        """Test TVShowNFO with lists."""
        nfo = TVShowNFO(
            title="Test Show",
            studio=["Studio A", "Studio B"],
            genre=["Action", "Drama"],
            country=["Japan", "USA"],
            tag=["anime", "popular"]
        )
        
        assert len(nfo.studio) == 2
        assert "Studio A" in nfo.studio
        assert len(nfo.genre) == 2
        assert len(nfo.country) == 2
        assert len(nfo.tag) == 2
    
    def test_tvshow_nfo_with_ratings(self):
        """Test TVShowNFO with ratings."""
        nfo = TVShowNFO(
            title="Test Show",
            ratings=[
                RatingInfo(name="tmdb", value=8.5, votes=1000, default=True),
                RatingInfo(name="imdb", value=8.2, votes=5000)
            ],
            userrating=9.0
        )
        
        assert len(nfo.ratings) == 2
        assert nfo.ratings[0].name == "tmdb"
        assert nfo.ratings[0].default is True
        assert nfo.userrating == 9.0
    
    def test_tvshow_nfo_invalid_userrating_rejected(self):
        """Test that userrating outside 0-10 is rejected."""
        with pytest.raises(ValidationError):
            TVShowNFO(title="Test", userrating=-1)
        
        with pytest.raises(ValidationError):
            TVShowNFO(title="Test", userrating=11)
    
    def test_tvshow_nfo_with_ids(self):
        """Test TVShowNFO with various IDs."""
        nfo = TVShowNFO(
            title="Test Show",
            tmdbid=12345,
            imdbid="tt1234567",
            tvdbid=67890,
            uniqueid=[
                UniqueID(type="tmdb", value="12345"),
                UniqueID(type="imdb", value="tt1234567", default=True)
            ]
        )
        
        assert nfo.tmdbid == 12345
        assert nfo.imdbid == "tt1234567"
        assert nfo.tvdbid == 67890
        assert len(nfo.uniqueid) == 2
    
    def test_tvshow_nfo_invalid_imdbid_rejected(self):
        """Test that invalid IMDB IDs are rejected."""
        with pytest.raises(ValidationError) as exc_info:
            TVShowNFO(title="Test", imdbid="12345")
        assert "must start with 'tt'" in str(exc_info.value)
        
        with pytest.raises(ValidationError) as exc_info:
            TVShowNFO(title="Test", imdbid="ttabc123")
        assert "followed by digits" in str(exc_info.value)
    
    def test_tvshow_nfo_valid_imdbid_accepted(self):
        """Test that valid IMDB IDs are accepted."""
        nfo = TVShowNFO(title="Test", imdbid="tt1234567")
        assert nfo.imdbid == "tt1234567"
    
    def test_tvshow_nfo_premiered_date_validation(self):
        """Test premiered date format validation."""
        # Valid format
        nfo = TVShowNFO(title="Test", premiered="2013-04-07")
        assert nfo.premiered == "2013-04-07"
        
        # Invalid formats
        with pytest.raises(ValidationError) as exc_info:
            TVShowNFO(title="Test", premiered="2013-4-7")
        assert "YYYY-MM-DD" in str(exc_info.value)
        
        with pytest.raises(ValidationError):
            TVShowNFO(title="Test", premiered="04/07/2013")
        
        with pytest.raises(ValidationError):
            TVShowNFO(title="Test", premiered="2013-13-01")  # Invalid month
    
    def test_tvshow_nfo_dateadded_validation(self):
        """Test dateadded format validation."""
        # Valid format
        nfo = TVShowNFO(title="Test", dateadded="2024-12-15 10:29:11")
        assert nfo.dateadded == "2024-12-15 10:29:11"
        
        # Invalid formats
        with pytest.raises(ValidationError) as exc_info:
            TVShowNFO(title="Test", dateadded="2024-12-15")
        assert "YYYY-MM-DD HH:MM:SS" in str(exc_info.value)
        
        with pytest.raises(ValidationError):
            TVShowNFO(title="Test", dateadded="2024-12-15 25:00:00")
    
    def test_tvshow_nfo_with_images(self):
        """Test TVShowNFO with image information."""
        nfo = TVShowNFO(
            title="Test Show",
            thumb=[
                ImageInfo(
                    url="https://image.tmdb.org/t/p/w500/poster.jpg",
                    aspect="poster"
                ),
                ImageInfo(
                    url="https://image.tmdb.org/t/p/original/logo.png",
                    aspect="clearlogo"
                )
            ],
            fanart=[
                ImageInfo(
                    url="https://image.tmdb.org/t/p/original/fanart.jpg"
                )
            ]
        )
        
        assert len(nfo.thumb) == 2
        assert nfo.thumb[0].aspect == "poster"
        assert len(nfo.fanart) == 1
    
    def test_tvshow_nfo_with_actors(self):
        """Test TVShowNFO with cast information."""
        nfo = TVShowNFO(
            title="Test Show",
            actors=[
                ActorInfo(
                    name="Actor One",
                    role="Main Character",
                    thumb="https://example.com/actor1.jpg",
                    tmdbid=111
                ),
                ActorInfo(
                    name="Actor Two",
                    role="Supporting Role"
                )
            ]
        )
        
        assert len(nfo.actors) == 2
        assert nfo.actors[0].name == "Actor One"
        assert nfo.actors[0].role == "Main Character"
        assert nfo.actors[1].tmdbid is None
    
    def test_tvshow_nfo_with_named_seasons(self):
        """Test TVShowNFO with named seasons."""
        nfo = TVShowNFO(
            title="Test Show",
            namedseason=[
                NamedSeason(number=1, name="First Season"),
                NamedSeason(number=2, name="Second Season")
            ]
        )
        
        assert len(nfo.namedseason) == 2
        assert nfo.namedseason[0].number == 1
    
    def test_tvshow_nfo_with_trailer(self):
        """Test TVShowNFO with trailer URL."""
        nfo = TVShowNFO(
            title="Test Show",
            trailer="https://www.youtube.com/watch?v=abc123"
        )
        
        assert nfo.trailer is not None
        assert "youtube.com" in str(nfo.trailer)
    
    def test_tvshow_nfo_watched_and_playcount(self):
        """Test TVShowNFO with viewing information."""
        nfo = TVShowNFO(
            title="Test Show",
            watched=True,
            playcount=5
        )
        
        assert nfo.watched is True
        assert nfo.playcount == 5
    
    def test_tvshow_nfo_negative_playcount_rejected(self):
        """Test that negative playcount is rejected."""
        with pytest.raises(ValidationError):
            TVShowNFO(title="Test", playcount=-1)
    
    def test_tvshow_nfo_serialization(self):
        """Test TVShowNFO can be serialized to dict."""
        nfo = TVShowNFO(
            title="Test Show",
            year=2020,
            genre=["Action", "Drama"],
            tmdbid=12345
        )
        
        data = nfo.model_dump()
        
        assert data["title"] == "Test Show"
        assert data["year"] == 2020
        assert data["genre"] == ["Action", "Drama"]
        assert data["tmdbid"] == 12345
        assert "showtitle" in data
        assert "originaltitle" in data
    
    def test_tvshow_nfo_deserialization(self):
        """Test TVShowNFO can be deserialized from dict."""
        data = {
            "title": "Test Show",
            "year": 2020,
            "genre": ["Action"],
            "tmdbid": 12345,
            "premiered": "2020-01-01"
        }
        
        nfo = TVShowNFO(**data)
        
        assert nfo.title == "Test Show"
        assert nfo.year == 2020
        assert nfo.genre == ["Action"]
        assert nfo.tmdbid == 12345
    
    def test_tvshow_nfo_special_characters_in_title(self):
        """Test TVShowNFO handles special characters."""
        nfo = TVShowNFO(
            title="Test: Show & Movie's \"Best\" <Episode>",
            plot="Special chars: < > & \" '"
        )
        
        assert nfo.title == "Test: Show & Movie's \"Best\" <Episode>"
        assert nfo.plot == "Special chars: < > & \" '"
    
    def test_tvshow_nfo_unicode_characters(self):
        """Test TVShowNFO handles Unicode characters."""
        nfo = TVShowNFO(
            title="進撃の巨人",
            originaltitle="Shingeki no Kyojin",
            plot="日本のアニメシリーズ"
        )
        
        assert nfo.title == "進撃の巨人"
        assert nfo.plot == "日本のアニメシリーズ"
    
    def test_tvshow_nfo_none_values(self):
        """Test TVShowNFO handles None values correctly."""
        nfo = TVShowNFO(
            title="Test Show",
            plot=None,
            year=None,
            tmdbid=None
        )
        
        assert nfo.title == "Test Show"
        assert nfo.plot is None
        assert nfo.year is None
        assert nfo.tmdbid is None
    
    def test_tvshow_nfo_empty_lists(self):
        """Test TVShowNFO with empty lists."""
        nfo = TVShowNFO(
            title="Test Show",
            genre=[],
            actors=[],
            ratings=[]
        )
        
        assert nfo.genre == []
        assert nfo.actors == []
        assert nfo.ratings == []
    
    @pytest.mark.parametrize("year", [1900, 2000, 2025, 2100])
    def test_tvshow_nfo_valid_years(self, year):
        """Test TVShowNFO accepts valid years."""
        nfo = TVShowNFO(title="Test Show", year=year)
        assert nfo.year == year
    
    @pytest.mark.parametrize("year", [1899, 2101, -1])
    def test_tvshow_nfo_invalid_years(self, year):
        """Test TVShowNFO rejects invalid years."""
        with pytest.raises(ValidationError):
            TVShowNFO(title="Test Show", year=year)
    
    @pytest.mark.parametrize("imdbid", [
        "tt0123456",
        "tt1234567",
        "tt12345678",
        "tt123456789"
    ])
    def test_tvshow_nfo_valid_imdbids(self, imdbid):
        """Test TVShowNFO accepts valid IMDB IDs."""
        nfo = TVShowNFO(title="Test Show", imdbid=imdbid)
        assert nfo.imdbid == imdbid
    
    @pytest.mark.parametrize("imdbid", [
        "123456",
        "tt",
        "ttabc123",
        "TT123456",
        "tt-123456"
    ])
    def test_tvshow_nfo_invalid_imdbids(self, imdbid):
        """Test TVShowNFO rejects invalid IMDB IDs."""
        with pytest.raises(ValidationError):
            TVShowNFO(title="Test Show", imdbid=imdbid)
