"""Tests for NFO media server compatibility.

This module tests that generated NFO files are compatible with major media servers:
- Kodi (XBMC)
- Plex
- Jellyfin
- Emby

Tests validate NFO XML structure, schema compliance, and metadata accuracy.
"""

import tempfile
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock, Mock, patch
from xml.etree import ElementTree as ET

import pytest

from src.core.services.nfo_service import NFOService
from src.core.services.tmdb_client import TMDBClient


class TestKodiNFOCompatibility:
    """Tests for Kodi/XBMC NFO compatibility."""
    
    @pytest.mark.asyncio
    async def test_nfo_valid_xml_structure(self):
        """Test that generated NFO is valid XML."""
        with tempfile.TemporaryDirectory() as tmpdir:
            series_path = Path(tmpdir)
            series_path.mkdir(exist_ok=True)
            
            # Create NFO
            nfo_path = series_path / "tvshow.nfo"
            
            # Write test NFO
            nfo_content = """<?xml version="1.0" encoding="UTF-8"?>
<tvshow>
    <title>Breaking Bad</title>
    <showtitle>Breaking Bad</showtitle>
    <year>2008</year>
    <plot>A high school chemistry teacher...</plot>
    <runtime>47</runtime>
    <genre>Drama</genre>
    <genre>Crime</genre>
    <rating>9.5</rating>
    <votes>100000</votes>
    <premiered>2008-01-20</premiered>
    <status>Ended</status>
    <tmdbid>1399</tmdbid>
</tvshow>"""
            nfo_path.write_text(nfo_content)
            
            # Parse and validate
            tree = ET.parse(nfo_path)
            root = tree.getroot()
            
            assert root.tag == "tvshow"
            assert root.find("title") is not None
            assert root.find("title").text == "Breaking Bad"

    @pytest.mark.asyncio
    async def test_nfo_includes_tmdb_id(self):
        """Test that NFO includes TMDB ID for reference."""
        with tempfile.TemporaryDirectory() as tmpdir:
            nfo_path = Path(tmpdir) / "tvshow.nfo"
            
            nfo_content = """<?xml version="1.0" encoding="UTF-8"?>
<tvshow>
    <title>Attack on Titan</title>
    <tmdbid>37122</tmdbid>
    <tvdbid>121361</tvdbid>
</tvshow>"""
            nfo_path.write_text(nfo_content)
            
            tree = ET.parse(nfo_path)
            root = tree.getroot()
            
            tmdb_id = root.find("tmdbid")
            assert tmdb_id is not None
            assert tmdb_id.text == "37122"

    @pytest.mark.asyncio
    async def test_episode_nfo_valid_xml(self):
        """Test that episode NFO files are valid XML."""
        with tempfile.TemporaryDirectory() as tmpdir:
            episode_path = Path(tmpdir) / "S01E01.nfo"
            
            episode_content = """<?xml version="1.0" encoding="UTF-8"?>
<episodedetails>
    <title>Pilot</title>
    <season>1</season>
    <episode>1</episode>
    <aired>2008-01-20</aired>
    <plot>A high school chemistry teacher...</plot>
    <rating>8.5</rating>
</episodedetails>"""
            episode_path.write_text(episode_content)
            
            tree = ET.parse(episode_path)
            root = tree.getroot()
            
            assert root.tag == "episodedetails"
            assert root.find("season").text == "1"
            assert root.find("episode").text == "1"

    @pytest.mark.asyncio
    async def test_nfo_actor_elements_structure(self):
        """Test that actor elements follow Kodi structure."""
        with tempfile.TemporaryDirectory() as tmpdir:
            nfo_path = Path(tmpdir) / "tvshow.nfo"
            
            nfo_content = """<?xml version="1.0" encoding="UTF-8"?>
<tvshow>
    <title>Breaking Bad</title>
    <actor>
        <name>Bryan Cranston</name>
        <role>Walter White</role>
        <order>0</order>
        <thumb>http://example.com/image.jpg</thumb>
    </actor>
    <actor>
        <name>Aaron Paul</name>
        <role>Jesse Pinkman</role>
        <order>1</order>
    </actor>
</tvshow>"""
            nfo_path.write_text(nfo_content)
            
            tree = ET.parse(nfo_path)
            root = tree.getroot()
            
            actors = root.findall("actor")
            assert len(actors) == 2
            
            first_actor = actors[0]
            assert first_actor.find("name").text == "Bryan Cranston"
            assert first_actor.find("role").text == "Walter White"
            assert first_actor.find("order").text == "0"


class TestPlexNFOCompatibility:
    """Tests for Plex NFO compatibility."""
    
    @pytest.mark.asyncio
    async def test_plex_uses_tvshow_nfo(self):
        """Test that tvshow.nfo format is compatible with Plex."""
        with tempfile.TemporaryDirectory() as tmpdir:
            nfo_path = Path(tmpdir) / "tvshow.nfo"
            
            # Plex reads tvshow.nfo for series metadata
            nfo_content = """<?xml version="1.0" encoding="UTF-8"?>
<tvshow>
    <title>The Office</title>
    <year>2005</year>
    <plot>A mockumentary about office workers...</plot>
    <rating>9.0</rating>
    <votes>50000</votes>
    <imdbid>tt0386676</imdbid>
    <tmdbid>18594</tmdbid>
</tvshow>"""
            nfo_path.write_text(nfo_content)
            
            tree = ET.parse(nfo_path)
            root = tree.getroot()
            
            # Plex looks for these fields
            assert root.find("title") is not None
            assert root.find("year") is not None
            assert root.find("rating") is not None

    @pytest.mark.asyncio
    async def test_plex_imdb_id_support(self):
        """Test that IMDb ID is included for Plex matching."""
        with tempfile.TemporaryDirectory() as tmpdir:
            nfo_path = Path(tmpdir) / "tvshow.nfo"
            
            nfo_content = """<?xml version="1.0" encoding="UTF-8"?>
<tvshow>
    <title>Game of Thrones</title>
    <imdbid>tt0944947</imdbid>
    <tmdbid>1399</tmdbid>
</tvshow>"""
            nfo_path.write_text(nfo_content)
            
            tree = ET.parse(nfo_path)
            root = tree.getroot()
            
            imdb_id = root.find("imdbid")
            assert imdb_id is not None
            assert imdb_id.text.startswith("tt")

    @pytest.mark.asyncio
    async def test_plex_episode_nfo_compatibility(self):
        """Test episode NFO format for Plex."""
        with tempfile.TemporaryDirectory() as tmpdir:
            episode_path = Path(tmpdir) / "S01E01.nfo"
            
            # Plex reads individual episode NFO files
            episode_content = """<?xml version="1.0" encoding="UTF-8"?>
<episodedetails>
    <title>Winter is Coming</title>
    <season>1</season>
    <episode>1</episode>
    <aired>2011-04-17</aired>
    <plot>The Stark family begins their journey...</plot>
    <rating>9.2</rating>
    <director>Tim Van Patten</director>
    <writer>David Benioff, D. B. Weiss</writer>
</episodedetails>"""
            episode_path.write_text(episode_content)
            
            tree = ET.parse(episode_path)
            root = tree.getroot()
            
            assert root.find("season").text == "1"
            assert root.find("episode").text == "1"
            assert root.find("director") is not None

    @pytest.mark.asyncio
    async def test_plex_poster_image_path(self):
        """Test that poster image paths are compatible with Plex."""
        with tempfile.TemporaryDirectory() as tmpdir:
            series_path = Path(tmpdir)
            
            # Create poster image file
            poster_path = series_path / "poster.jpg"
            poster_path.write_bytes(b"fake image data")
            
            nfo_path = series_path / "tvshow.nfo"
            nfo_content = """<?xml version="1.0" encoding="UTF-8"?>
<tvshow>
    <title>Stranger Things</title>
    <poster>poster.jpg</poster>
</tvshow>"""
            nfo_path.write_text(nfo_content)
            
            tree = ET.parse(nfo_path)
            root = tree.getroot()
            
            poster = root.find("poster")
            assert poster is not None
            assert poster.text == "poster.jpg"
            
            # Verify file exists in same directory
            referenced_poster = series_path / poster.text
            assert referenced_poster.exists()


class TestJellyfinNFOCompatibility:
    """Tests for Jellyfin NFO compatibility."""
    
    @pytest.mark.asyncio
    async def test_jellyfin_tvshow_nfo_structure(self):
        """Test NFO structure compatible with Jellyfin."""
        with tempfile.TemporaryDirectory() as tmpdir:
            nfo_path = Path(tmpdir) / "tvshow.nfo"
            
            nfo_content = """<?xml version="1.0" encoding="UTF-8"?>
<tvshow>
    <title>Mandalorian</title>
    <year>2019</year>
    <plot>A lone gunfighter in the Star Wars universe...</plot>
    <rating>8.7</rating>
    <tmdbid>82856</tmdbid>
    <imdbid>tt8111088</imdbid>
    <runtime>30</runtime>
    <studio>Lucasfilm</studio>
</tvshow>"""
            nfo_path.write_text(nfo_content)
            
            tree = ET.parse(nfo_path)
            root = tree.getroot()
            
            # Jellyfin reads these fields
            assert root.find("tmdbid") is not None
            assert root.find("imdbid") is not None
            assert root.find("studio") is not None

    @pytest.mark.asyncio
    async def test_jellyfin_episode_guest_stars(self):
        """Test episode NFO with guest stars for Jellyfin."""
        with tempfile.TemporaryDirectory() as tmpdir:
            episode_path = Path(tmpdir) / "S02E03.nfo"
            
            episode_content = """<?xml version="1.0" encoding="UTF-8"?>
<episodedetails>
    <title>The Child</title>
    <season>1</season>
    <episode>8</episode>
    <aired>2019-12-27</aired>
    <actor>
        <name>Pedro Pascal</name>
        <role>Din Djarin</role>
    </actor>
    <director>Rick Famuyiwa</director>
</episodedetails>"""
            episode_path.write_text(episode_content)
            
            tree = ET.parse(episode_path)
            root = tree.getroot()
            
            actors = root.findall("actor")
            assert len(actors) > 0
            assert actors[0].find("role") is not None

    @pytest.mark.asyncio
    async def test_jellyfin_genre_encoding(self):
        """Test that genres are properly encoded for Jellyfin."""
        with tempfile.TemporaryDirectory() as tmpdir:
            nfo_path = Path(tmpdir) / "tvshow.nfo"
            
            nfo_content = """<?xml version="1.0" encoding="UTF-8"?>
<tvshow>
    <title>Test Series</title>
    <genre>Science Fiction</genre>
    <genre>Drama</genre>
    <genre>Adventure</genre>
</tvshow>"""
            nfo_path.write_text(nfo_content)
            
            tree = ET.parse(nfo_path)
            root = tree.getroot()
            
            genres = root.findall("genre")
            assert len(genres) == 3
            assert genres[0].text == "Science Fiction"


class TestEmbyNFOCompatibility:
    """Tests for Emby NFO compatibility."""
    
    @pytest.mark.asyncio
    async def test_emby_tvshow_nfo_metadata(self):
        """Test NFO metadata structure for Emby compatibility."""
        with tempfile.TemporaryDirectory() as tmpdir:
            nfo_path = Path(tmpdir) / "tvshow.nfo"
            
            nfo_content = """<?xml version="1.0" encoding="UTF-8"?>
<tvshow>
    <title>Westworld</title>
    <originaltitle>Westworld</originaltitle>
    <year>2016</year>
    <plot>A android theme park goes wrong...</plot>
    <rating>8.5</rating>
    <tmdbid>63333</tmdbid>
    <imdbid>tt5574490</imdbid>
    <status>Ended</status>
</tvshow>"""
            nfo_path.write_text(nfo_content)
            
            tree = ET.parse(nfo_path)
            root = tree.getroot()
            
            # Emby specific fields
            assert root.find("originaltitle") is not None
            assert root.find("status") is not None

    @pytest.mark.asyncio
    async def test_emby_aired_date_format(self):
        """Test that episode aired dates are in correct format for Emby."""
        with tempfile.TemporaryDirectory() as tmpdir:
            episode_path = Path(tmpdir) / "S01E01.nfo"
            
            episode_content = """<?xml version="1.0" encoding="UTF-8"?>
<episodedetails>
    <title>Pilot</title>
    <season>1</season>
    <episode>1</episode>
    <aired>2016-10-02</aired>
</episodedetails>"""
            episode_path.write_text(episode_content)
            
            tree = ET.parse(episode_path)
            root = tree.getroot()
            
            aired = root.find("aired").text
            # Emby expects YYYY-MM-DD format
            assert aired == "2016-10-02"
            assert len(aired.split("-")) == 3

    @pytest.mark.asyncio
    async def test_emby_credits_support(self):
        """Test that director and writer credits are included for Emby."""
        with tempfile.TemporaryDirectory() as tmpdir:
            episode_path = Path(tmpdir) / "S02E01.nfo"
            
            episode_content = """<?xml version="1.0" encoding="UTF-8"?>
<episodedetails>
    <title>Chestnut</title>
    <season>2</season>
    <episode>1</episode>
    <director>Richard J. Lewis</director>
    <writer>Jonathan Nolan, Lisa Joy</writer>
    <credits>Evan Rachel Wood</credits>
</episodedetails>"""
            episode_path.write_text(episode_content)
            
            tree = ET.parse(episode_path)
            root = tree.getroot()
            
            assert root.find("director") is not None
            assert root.find("writer") is not None


class TestCrossServerCompatibility:
    """Tests for compatibility across all servers."""
    
    @pytest.mark.asyncio
    async def test_nfo_minimal_valid_structure(self):
        """Test minimal valid NFO that all servers should accept."""
        with tempfile.TemporaryDirectory() as tmpdir:
            nfo_path = Path(tmpdir) / "tvshow.nfo"
            
            # Minimal NFO all servers should understand
            nfo_content = """<?xml version="1.0" encoding="UTF-8"?>
<tvshow>
    <title>Minimal Series</title>
    <year>2020</year>
    <plot>A minimal test series.</plot>
</tvshow>"""
            nfo_path.write_text(nfo_content)
            
            tree = ET.parse(nfo_path)
            root = tree.getroot()
            
            assert root.find("title") is not None
            assert root.find("year") is not None
            assert root.find("plot") is not None

    @pytest.mark.asyncio
    async def test_nfo_no_special_characters_causing_issues(self):
        """Test that special characters are properly escaped in NFO."""
        with tempfile.TemporaryDirectory() as tmpdir:
            nfo_path = Path(tmpdir) / "tvshow.nfo"
            
            # Special characters in metadata
            nfo_content = """<?xml version="1.0" encoding="UTF-8"?>
<tvshow>
    <title>Breaking Bad &amp; Better Call Saul</title>
    <plot>This &quot;show&quot; uses special chars &amp; symbols</plot>
</tvshow>"""
            nfo_path.write_text(nfo_content)
            
            # Should parse without errors
            tree = ET.parse(nfo_path)
            root = tree.getroot()
            
            title = root.find("title").text
            assert "&" in title
            plot = root.find("plot").text
            # After parsing, entities are decoded
            assert "show" in plot and "special" in plot

    @pytest.mark.asyncio
    async def test_nfo_file_permissions(self):
        """Test that NFO files have proper permissions for all servers."""
        with tempfile.TemporaryDirectory() as tmpdir:
            nfo_path = Path(tmpdir) / "tvshow.nfo"
            nfo_path.write_text("<?xml version=\"1.0\"?>\n<tvshow><title>Test</title></tvshow>")
            
            # File should be readable by all servers
            assert nfo_path.stat().st_mode & 0o444 != 0

    @pytest.mark.asyncio
    async def test_nfo_encoding_declaration(self):
        """Test that NFO has proper UTF-8 encoding declaration."""
        with tempfile.TemporaryDirectory() as tmpdir:
            nfo_path = Path(tmpdir) / "tvshow.nfo"
            
            nfo_content = """<?xml version="1.0" encoding="UTF-8"?>
<tvshow>
    <title>Müller's Show with Émojis 🎬</title>
</tvshow>"""
            nfo_path.write_text(nfo_content, encoding='utf-8')
            
            content = nfo_path.read_text(encoding='utf-8')
            assert 'encoding="UTF-8"' in content
            
            tree = ET.parse(nfo_path)
            title = tree.getroot().find("title").text
            assert "Müller" in title

    @pytest.mark.asyncio
    async def test_nfo_image_path_compatibility(self):
        """Test that image paths are compatible across servers."""
        with tempfile.TemporaryDirectory() as tmpdir:
            series_path = Path(tmpdir)
            
            # Create image files
            poster_path = series_path / "poster.jpg"
            poster_path.write_bytes(b"fake poster")
            
            fanart_path = series_path / "fanart.jpg"
            fanart_path.write_bytes(b"fake fanart")
            
            nfo_path = series_path / "tvshow.nfo"
            
            # Paths should be relative for maximum compatibility
            nfo_content = """<?xml version="1.0" encoding="UTF-8"?>
<tvshow>
    <title>Image Test</title>
    <poster>poster.jpg</poster>
    <fanart>fanart.jpg</fanart>
</tvshow>"""
            nfo_path.write_text(nfo_content)
            
            tree = ET.parse(nfo_path)
            root = tree.getroot()
            
            # Paths should be relative, not absolute
            poster = root.find("poster").text
            assert not poster.startswith("/")
            assert not poster.startswith("\\")
