"""Unit tests for NFO generator."""

import pytest
from lxml import etree

from src.core.entities.nfo_models import (
    ActorInfo,
    ImageInfo,
    RatingInfo,
    TVShowNFO,
    UniqueID,
)
from src.core.utils.nfo_generator import generate_tvshow_nfo, validate_nfo_xml


class TestGenerateTVShowNFO:
    """Test generate_tvshow_nfo function."""
    
    def test_generate_minimal_nfo(self):
        """Test generation with minimal required fields."""
        nfo = TVShowNFO(
            title="Test Show",
            plot="A test show"
        )
        
        xml_string = generate_tvshow_nfo(nfo)
        
        # Actual implementation uses 'standalone="yes"' in declaration
        assert xml_string.startswith('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>')
        assert "<title>Test Show</title>" in xml_string
        assert "<plot>A test show</plot>" in xml_string
    
    def test_generate_complete_nfo(self):
        """Test generation with all fields populated."""
        nfo = TVShowNFO(
            title="Complete Show",
            originaltitle="Original Title",
            year=2020,
            plot="Complete test",
            runtime=45,
            premiered="2020-01-15",
            status="Continuing",
            genre=["Action", "Drama"],
            studio=["Studio 1"],
            country=["USA"],
            ratings=[RatingInfo(
                name="themoviedb",
                value=8.5,
                votes=1000,
                max_rating=10,
                default=True
            )],
            actors=[ActorInfo(
                name="Test Actor",
                role="Main Character"
            )],
            thumb=[ImageInfo(url="https://test.com/poster.jpg")],
            uniqueid=[UniqueID(type="tmdb", value="12345")]
        )
        
        xml_string = generate_tvshow_nfo(nfo)
        
        # Verify all elements present
        assert "<title>Complete Show</title>" in xml_string
        assert "<originaltitle>Original Title</originaltitle>" in xml_string
        assert "<year>2020</year>" in xml_string
        assert "<runtime>45</runtime>" in xml_string
        assert "<premiered>2020-01-15</premiered>" in xml_string
        assert "<status>Continuing</status>" in xml_string
        assert "<genre>Action</genre>" in xml_string
        assert "<genre>Drama</genre>" in xml_string
        assert "<studio>Studio 1</studio>" in xml_string
        assert "<country>USA</country>" in xml_string
        assert "<name>Test Actor</name>" in xml_string
        assert "<role>Main Character</role>" in xml_string
    
    def test_generate_nfo_with_ratings(self):
        """Test NFO with multiple ratings."""
        nfo = TVShowNFO(
            title="Rated Show",
            plot="Test",
            ratings=[
                RatingInfo(
                    name="themoviedb",
                    value=8.5,
                    votes=1000,
                    max_rating=10,
                    default=True
                ),
                RatingInfo(
                    name="imdb",
                    value=8.2,
                    votes=5000,
                    max_rating=10,
                    default=False
                )
            ]
        )
        
        xml_string = generate_tvshow_nfo(nfo)
        
        assert '<ratings>' in xml_string
        # Actual implementation includes max attribute and only adds default when True
        assert '<rating name="themoviedb" max="10" default="true">' in xml_string
        assert '<value>8.5</value>' in xml_string
        assert '<votes>1000</votes>' in xml_string
        assert '<rating name="imdb" max="10">' in xml_string
    
    def test_generate_nfo_with_actors(self):
        """Test NFO with multiple actors."""
        nfo = TVShowNFO(
            title="Cast Show",
            plot="Test",
            actors=[
                ActorInfo(name="Actor 1", role="Hero"),
                ActorInfo(name="Actor 2", role="Villain", thumb="https://test.com/actor2.jpg")
            ]
        )
        
        xml_string = generate_tvshow_nfo(nfo)
        
        assert '<actor>' in xml_string
        assert '<name>Actor 1</name>' in xml_string
        assert '<role>Hero</role>' in xml_string
        assert '<name>Actor 2</name>' in xml_string
        assert '<thumb>https://test.com/actor2.jpg</thumb>' in xml_string
    
    def test_generate_nfo_with_images(self):
        """Test NFO with various image types."""
        nfo = TVShowNFO(
            title="Image Show",
            plot="Test",
            thumb=[
                ImageInfo(url="https://test.com/poster.jpg", aspect="poster"),
                ImageInfo(url="https://test.com/logo.png", aspect="clearlogo")
            ],
            fanart=[
                ImageInfo(url="https://test.com/fanart.jpg")
            ]
        )
        
        xml_string = generate_tvshow_nfo(nfo)
        
        assert '<thumb aspect="poster">https://test.com/poster.jpg</thumb>' in xml_string
        assert '<thumb aspect="clearlogo">https://test.com/logo.png</thumb>' in xml_string
        assert '<fanart>' in xml_string
        assert 'https://test.com/fanart.jpg' in xml_string
    
    def test_generate_nfo_with_unique_ids(self):
        """Test NFO with multiple unique IDs."""
        nfo = TVShowNFO(
            title="ID Show",
            plot="Test",
            uniqueid=[
                UniqueID(type="tmdb", value="12345", default=False),
                UniqueID(type="tvdb", value="67890", default=True),
                UniqueID(type="imdb", value="tt1234567", default=False)
            ]
        )
        
        xml_string = generate_tvshow_nfo(nfo)
        
        # Actual implementation only adds default="true" when default is True, omits attribute when False
        assert '<uniqueid type="tmdb">12345</uniqueid>' in xml_string
        assert '<uniqueid type="tvdb" default="true">67890</uniqueid>' in xml_string
        assert '<uniqueid type="imdb">tt1234567</uniqueid>' in xml_string
    
    def test_generate_nfo_escapes_special_chars(self):
        """Test that special XML characters are escaped."""
        nfo = TVShowNFO(
            title="Show <with> & special \"chars\"",
            plot="Plot with <tags> & ampersand"
        )
        
        xml_string = generate_tvshow_nfo(nfo)
        
        # XML should escape special characters
        assert "&lt;" in xml_string or "<title>" in xml_string
        assert "&amp;" in xml_string or "&" in xml_string
    
    def test_generate_nfo_valid_xml(self):
        """Test that generated XML is valid."""
        nfo = TVShowNFO(
            title="Valid Show",
            plot="Test",
            year=2020,
            genre=["Action"],
            ratings=[RatingInfo(name="test", value=8.0)]
        )
        
        xml_string = generate_tvshow_nfo(nfo)
        
        # Should be parseable as XML
        root = etree.fromstring(xml_string.encode('utf-8'))
        assert root.tag == "tvshow"
    
    def test_generate_nfo_none_values_omitted(self):
        """Test that None values are omitted from XML."""
        nfo = TVShowNFO(
            title="Sparse Show",
            plot="Test",
            year=None,
            runtime=None,
            premiered=None
        )
        
        xml_string = generate_tvshow_nfo(nfo)
        
        # None values should not appear in XML
        assert "<year>" not in xml_string
        assert "<runtime>" not in xml_string
        assert "<premiered>" not in xml_string


class TestValidateNFOXML:
    """Test validate_nfo_xml function."""
    
    def test_validate_valid_xml(self):
        """Test validation of valid XML."""
        nfo = TVShowNFO(title="Test", plot="Test")
        xml_string = generate_tvshow_nfo(nfo)
        
        # Should not raise exception
        validate_nfo_xml(xml_string)
    
    def test_validate_invalid_xml(self):
        """Test validation of invalid XML."""
        invalid_xml = "<?xml version='1.0'?><tvshow><title>Unclosed"
        
        # validate_nfo_xml returns False for invalid XML, doesn't raise
        result = validate_nfo_xml(invalid_xml)
        assert result is False
    
    def test_validate_missing_tvshow_root(self):
        """Test validation accepts any well-formed XML (doesn't check root)."""
        valid_xml = '<?xml version="1.0"?><movie><title>Test</title></movie>'
        
        # validate_nfo_xml only checks if XML is well-formed, not structure
        result = validate_nfo_xml(valid_xml)
        assert result is True
    
    def test_validate_empty_string(self):
        """Test validation rejects empty string."""
        result = validate_nfo_xml("")
        assert result is False
    
    def test_validate_well_formed_structure(self):
        """Test validation accepts well-formed structure."""
        xml = """<?xml version="1.0" encoding="UTF-8"?>
        <tvshow>
            <title>Test Show</title>
            <plot>Test plot</plot>
            <year>2020</year>
        </tvshow>
        """
        
        validate_nfo_xml(xml)


class TestNFOGeneratorEdgeCases:
    """Test edge cases in NFO generation."""
    
    def test_empty_lists(self):
        """Test generation with empty lists."""
        nfo = TVShowNFO(
            title="Empty Lists",
            plot="Test",
            genre=[],
            studio=[],
            actors=[]
        )
        
        xml_string = generate_tvshow_nfo(nfo)
        
        # Should generate valid XML even with empty lists
        root = etree.fromstring(xml_string.encode('utf-8'))
        assert root.tag == "tvshow"
    
    def test_unicode_characters(self):
        """Test handling of Unicode characters."""
        nfo = TVShowNFO(
            title="アニメ Show 中文",
            plot="Plot with émojis 🎬 and spëcial çhars"
        )
        
        xml_string = generate_tvshow_nfo(nfo)
        
        # Should encode Unicode properly
        assert "アニメ" in xml_string
        assert "中文" in xml_string
        assert "émojis" in xml_string
    
    def test_very_long_plot(self):
        """Test handling of very long plot text."""
        long_plot = "A" * 10000
        nfo = TVShowNFO(
            title="Long Plot",
            plot=long_plot
        )
        
        xml_string = generate_tvshow_nfo(nfo)
        
        assert long_plot in xml_string
    
    def test_multiple_studios(self):
        """Test handling multiple studios."""
        nfo = TVShowNFO(
            title="Multi Studio",
            plot="Test",
            studio=["Studio A", "Studio B", "Studio C"]
        )
        
        xml_string = generate_tvshow_nfo(nfo)
        
        assert xml_string.count("<studio>") == 3
        assert "<studio>Studio A</studio>" in xml_string
        assert "<studio>Studio B</studio>" in xml_string
        assert "<studio>Studio C</studio>" in xml_string
    
    def test_special_date_formats(self):
        """Test various date format inputs."""
        nfo = TVShowNFO(
            title="Date Test",
            plot="Test",
            premiered="2020-01-01"
        )
        
        xml_string = generate_tvshow_nfo(nfo)
        
        assert "<premiered>2020-01-01</premiered>" in xml_string


class TestFSKRatingGeneration:
    """Test FSK rating generation in NFO XML."""
    
    def test_generate_nfo_with_fsk_rating(self):
        """Test NFO generation with FSK rating."""
        nfo = TVShowNFO(
            title="FSK Show",
            plot="Test",
            fsk="FSK 12",
            mpaa="TV-14"
        )
        
        xml_string = generate_tvshow_nfo(nfo)
        
        # Should use FSK rating when available and preferred (default)
        assert "<mpaa>FSK 12</mpaa>" in xml_string
    
    def test_generate_nfo_fsk_preferred_over_mpaa(self):
        """Test that FSK is preferred over MPAA when both present."""
        nfo = TVShowNFO(
            title="FSK Priority Show",
            plot="Test",
            fsk="FSK 16",
            mpaa="TV-MA"
        )
        
        xml_string = generate_tvshow_nfo(nfo)
        
        # FSK should be in mpaa tag, not TV-MA
        assert "<mpaa>FSK 16</mpaa>" in xml_string
        assert "TV-MA" not in xml_string
    
    def test_generate_nfo_fallback_to_mpaa(self):
        """Test fallback to MPAA when FSK not available."""
        nfo = TVShowNFO(
            title="MPAA Show",
            plot="Test",
            fsk=None,
            mpaa="TV-PG"
        )
        
        xml_string = generate_tvshow_nfo(nfo)
        
        # Should use MPAA when FSK not available
        assert "<mpaa>TV-PG</mpaa>" in xml_string
    
    def test_generate_nfo_with_all_fsk_values(self):
        """Test NFO generation with all possible FSK values."""
        fsk_values = ["FSK 0", "FSK 6", "FSK 12", "FSK 16", "FSK 18"]
        
        for fsk in fsk_values:
            nfo = TVShowNFO(
                title=f"FSK {fsk} Show",
                plot="Test",
                fsk=fsk
            )
            
            xml_string = generate_tvshow_nfo(nfo)
            assert f"<mpaa>{fsk}</mpaa>" in xml_string
    
    def test_generate_nfo_no_rating(self):
        """Test NFO generation when neither FSK nor MPAA is available."""
        nfo = TVShowNFO(
            title="No Rating Show",
            plot="Test",
            fsk=None,
            mpaa=None
        )
        
        xml_string = generate_tvshow_nfo(nfo)
        
        # mpaa tag should not be present
        assert "<mpaa>" not in xml_string
