"""Tests for Sonarr webhook integration"""
import pytest
import json
from unittest.mock import Mock, AsyncMock, patch
from datetime import datetime

from app.models.sonarr import (
    SonarrWebhookPayload,
    SonarrEventType,
    SonarrSeries,
    SonarrEpisode,
    SonarrConfig,
    SonarrMapping,
    SonarrDownloadRequest
)
from app.sonarr_handler import SonarrHandler, get_sonarr_handler


# ==================== FIXTURES ====================

@pytest.fixture
def sample_sonarr_series():
    """Sample Sonarr series data"""
    return {
        "tvdbId": 12345,
        "title": "Naruto Shippuden",
        "sortTitle": "naruto shippuden",
        "status": "continuing",
        "ended": False,
        "overview": "Test overview",
        "network": "TV Tokyo",
        "airTime": "19:00",
        "images": [],
        "seasons": [1, 2, 3],
        "year": 2007,
        "path": "/anime/naruto",
        "qualityProfileId": 1,
        "languageProfileId": 1,
        "seasonFolder": True,
        "monitored": True,
        "useSceneNumbering": False,
        "runtime": 24,
        "tvRageId": 123,
        "tvMazeId": 456,
        "firstAired": "2007-02-15T00:00:00Z",
        "seriesType": "standard",
        "cleanTitle": "narutoshippuden",
        "imdbId": "tt0988824",
        "titleSlug": "naruto-shippuden",
        "certification": "TV-14",
        "genres": ["Action", "Adventure"],
        "tags": [],
        "added": "2023-01-01T00:00:00Z",
        "ratings": {"votes": 100, "value": 8.5},
        "id": 1
    }


@pytest.fixture
def sample_sonarr_episode():
    """Sample Sonarr episode data"""
    return {
        "seriesId": 12345,
        "episodeFileId": 1,
        "seasonNumber": 1,
        "episodeNumber": 1,
        "title": "Homecoming",
        "airDate": "2007-02-15",
        "airDateUtc": "2007-02-15T14:00:00Z",
        "overview": "Episode overview",
        "hasFile": True,
        "monitored": True,
        "absoluteEpisodeNumber": 1,
        "unverifiedSceneNumbering": False,
        "id": 1
    }


@pytest.fixture
def sample_grab_payload(sample_sonarr_series, sample_sonarr_episode):
    """Sample Grab event payload"""
    return {
        "eventType": "Grab",
        "instanceName": "Sonarr",
        "applicationUrl": "http://localhost:8989",
        "series": sample_sonarr_series,
        "episodes": [sample_sonarr_episode],
        "release": {
            "indexer": "test-indexer",
            "releaseTitle": "Naruto Shippuden S01E01 test",
            "quality": {
                "quality": {"name": "1080p", "id": 7},
                "revision": {"version": 1, "real": 0}
            }
        }
    }


@pytest.fixture
def sample_sonarr_config():
    """Sample Sonarr configuration"""
    return SonarrConfig(
        webhook_enabled=True,
        webhook_secret="test-secret",
        auto_download_enabled=True,
        default_language="vostfr",
        default_quality="1080p",
        default_provider="anime-sama",
        verify_hmac=True,
        log_webhooks=True
    )


@pytest.fixture
def temp_sonarr_handler(temp_dir):
    """Create SonarrHandler with temporary storage"""
    config_path = temp_dir / "sonarr_config.json"
    mappings_path = temp_dir / "sonarr_mappings.json"
    return SonarrHandler(str(config_path), str(mappings_path))


@pytest.fixture
def sample_mapping():
    """Sample Sonarr to anime mapping"""
    return SonarrMapping(
        sonarr_series_id=12345,
        sonarr_title="Naruto Shippuden",
        anime_provider="anime-sama",
        anime_url="https://anime-sama.si/catalogue/naruto-shippuden/saison1/vostfr/",
        anime_title="Naruto Shippuden",
        lang="vostfr",
        quality_preference="1080p",
        auto_download=True
    )


# ==================== MODEL TESTS ====================

class TestSonarrModels:
    """Test Sonarr Pydantic models"""

    def test_sonarr_config_validation(self):
        """Test SonarrConfig model validation"""
        config = SonarrConfig(
            webhook_enabled=True,
            webhook_secret="secret123",
            auto_download_enabled=True
        )
        assert config.webhook_enabled is True
        assert config.webhook_secret == "secret123"
        assert config.auto_download_enabled is True

    def test_sonarr_mapping_validation(self):
        """Test SonarrMapping model validation"""
        mapping = SonarrMapping(
            sonarr_series_id=123,
            sonarr_title="Test Anime",
            anime_provider="anime-sama",
            anime_url="https://test.com/anime/",
            anime_title="Test Anime",
            lang="vostfr"
        )
        assert mapping.sonarr_series_id == 123
        assert mapping.anime_provider == "anime-sama"
        assert mapping.auto_download is True  # Default value

    def test_sonarr_download_request_validation(self):
        """Test SonarrDownloadRequest model validation"""
        request = SonarrDownloadRequest(
            sonarr_series_id=123,
            sonarr_title="Test Anime",
            season_number=1,
            episode_number=5,
            quality="1080p",
            lang="vostfr",
            provider="anime-sama"
        )
        assert request.season_number == 1
        assert request.episode_number == 5
        assert request.quality == "1080p"

    def test_grab_payload_validation(self, sample_grab_payload):
        """Test SonarrWebhookPayload validation for Grab event"""
        payload = SonarrWebhookPayload(**sample_grab_payload)
        assert payload.eventType == SonarrEventType.GRAB
        assert payload.series is not None
        assert payload.episodes is not None
        assert len(payload.episodes) == 1
        assert payload.series.tvdbId == 12345

    def test_test_payload_validation(self):
        """Test SonarrWebhookPayload validation for Test event"""
        payload_data = {
            "eventType": "Test",
            "instanceName": "Sonarr",
            "applicationUrl": "http://localhost:8989"
        }
        payload = SonarrWebhookPayload(**payload_data)
        assert payload.eventType == SonarrEventType.TEST


# ==================== HANDLER TESTS ====================

class TestSonarrHandler:
    """Test SonarrHandler functionality"""

    def test_handler_initialization(self, temp_sonarr_handler):
        """Test SonarrHandler initialization"""
        assert temp_sonarr_handler.config is not None
        assert isinstance(temp_sonarr_handler.mappings, list)
        assert len(temp_sonarr_handler.mappings) == 0

    def test_config_persistence(self, temp_sonarr_handler, sample_sonarr_config):
        """Test configuration save/load"""
        # Update config
        temp_sonarr_handler.update_config(sample_sonarr_config)

        # Create new handler instance to test persistence
        config_path = temp_sonarr_handler.config_path
        mappings_path = temp_sonarr_handler.mappings_path
        new_handler = SonarrHandler(str(config_path), str(mappings_path))

        assert new_handler.config.webhook_enabled == sample_sonarr_config.webhook_enabled
        assert new_handler.config.webhook_secret == sample_sonarr_config.webhook_secret

    def test_add_mapping(self, temp_sonarr_handler, sample_mapping):
        """Test adding a new mapping"""
        result = temp_sonarr_handler.add_mapping(sample_mapping)
        assert len(temp_sonarr_handler.mappings) == 1
        assert result.sonarr_series_id == sample_mapping.sonarr_series_id
        assert result.anime_title == sample_mapping.anime_title

    def test_get_mapping(self, temp_sonarr_handler, sample_mapping):
        """Test retrieving a specific mapping"""
        temp_sonarr_handler.add_mapping(sample_mapping)
        result = temp_sonarr_handler.get_mapping(12345)
        assert result is not None
        assert result.anime_title == "Naruto Shippuden"

    def test_get_nonexistent_mapping(self, temp_sonarr_handler):
        """Test retrieving a non-existent mapping"""
        result = temp_sonarr_handler.get_mapping(99999)
        assert result is None

    def test_delete_mapping(self, temp_sonarr_handler, sample_mapping):
        """Test deleting a mapping"""
        temp_sonarr_handler.add_mapping(sample_mapping)
        assert len(temp_sonarr_handler.mappings) == 1

        success = temp_sonarr_handler.delete_mapping(12345)
        assert success is True
        assert len(temp_sonarr_handler.mappings) == 0

    def test_delete_nonexistent_mapping(self, temp_sonarr_handler):
        """Test deleting a non-existent mapping"""
        success = temp_sonarr_handler.delete_mapping(99999)
        assert success is False

    def test_update_mapping(self, temp_sonarr_handler, sample_mapping):
        """Test updating an existing mapping"""
        temp_sonarr_handler.add_mapping(sample_mapping)

        # Update with same series ID
        updated_mapping = SonarrMapping(
            sonarr_series_id=12345,
            sonarr_title="Naruto Shippuden",
            anime_provider="neko-sama",
            anime_url="https://neko-sama.fr/anime/naruto-shippuden",
            anime_title="Naruto Shippuden (Updated)",
            lang="vf"
        )

        result = temp_sonarr_handler.add_mapping(updated_mapping)
        assert len(temp_sonarr_handler.mappings) == 1  # Still only one
        assert result.anime_provider == "neko-sama"
        assert result.anime_title == "Naruto Shippuden (Updated)"

    def test_hmac_verification_valid(self, temp_sonarr_handler, sample_sonarr_config):
        """Test HMAC verification with valid signature"""
        import hmac
        import hashlib

        temp_sonarr_handler.update_config(sample_sonarr_config)

        # Create valid signature
        payload = b'{"test": "data"}'
        signature = hmac.new(
            sample_sonarr_config.webhook_secret.encode(),
            payload,
            hashlib.sha256
        ).hexdigest()

        result = temp_sonarr_handler.verify_hmac(payload, f"sha256={signature}")
        assert result is True

    def test_hmac_verification_invalid(self, temp_sonarr_handler, sample_sonarr_config):
        """Test HMAC verification with invalid signature"""
        temp_sonarr_handler.update_config(sample_sonarr_config)

        payload = b'{"test": "data"}'
        result = temp_sonarr_handler.verify_hmac(payload, "sha256=invalid")
        assert result is False

    def test_hmac_verification_disabled(self, temp_sonarr_handler):
        """Test HMAC verification when disabled"""
        temp_sonarr_handler.config.verify_hmac = False

        payload = b'{"test": "data"}'
        result = temp_sonarr_handler.verify_hmac(payload, "invalid")
        assert result is True  # Should pass when verification disabled

    def test_match_score_calculation(self, temp_sonarr_handler):
        """Test match score calculation"""
        # Exact match
        score1 = temp_sonarr_handler._calculate_match_score("Naruto", "Naruto")
        assert score1 == 1.0

        # Partial match
        score2 = temp_sonarr_handler._calculate_match_score("Naruto Shippuden", "Naruto Shippuden")
        assert score2 == 1.0

        # Contains
        score3 = temp_sonarr_handler._calculate_match_score("Naruto", "Naruto Shippuden")
        assert score3 > 0.5

        # No match
        score4 = temp_sonarr_handler._calculate_match_score("One Piece", "Naruto")
        assert score4 == 0.0


# ==================== WEBHOOK PROCESSING TESTS ====================

@pytest.mark.skip(reason="Test does not match current implementation")
class TestWebhookProcessing:
    """Test webhook processing"""

    @pytest.mark.asyncio
    async def test_process_grab_event_with_mapping(
        self, temp_sonarr_handler, sample_grab_payload, sample_mapping
    ):
        """Test processing Grab event with valid mapping"""
        temp_sonarr_handler.add_mapping(sample_mapping)
        temp_sonarr_handler.config.auto_download_enabled = True
        temp_sonarr_handler.config.webhook_enabled = True

        payload = SonarrWebhookPayload(**sample_grab_payload)
        result = await temp_sonarr_handler.process_webhook(payload)

        assert result["status"] == "processing"
        assert "mapping" in result
        assert result["mapping"] == "Naruto Shippuden"
        assert result["downloads_queued"] == 1

    @pytest.mark.asyncio
    async def test_process_grab_event_without_mapping(
        self, temp_sonarr_handler, sample_grab_payload
    ):
        """Test processing Grab event without mapping"""
        temp_sonarr_handler.config.auto_download_enabled = True
        temp_sonarr_handler.config.webhook_enabled = True

        payload = SonarrWebhookPayload(**sample_grab_payload)
        result = await temp_sonarr_handler.process_webhook(payload)

        assert result["status"] == "no_mapping"
        assert "series" in result

    @pytest.mark.asyncio
    async def test_process_grab_event_auto_disabled(
        self, temp_sonarr_handler, sample_grab_payload, sample_mapping
    ):
        """Test processing Grab event when auto-download is disabled"""
        temp_sonarr_handler.add_mapping(sample_mapping)
        temp_sonarr_handler.config.auto_download_enabled = False
        temp_sonarr_handler.config.webhook_enabled = True

        payload = SonarrWebhookPayload(**sample_grab_payload)
        result = await temp_sonarr_handler.process_webhook(payload)

        assert result["status"] == "ignored"
        assert result["reason"] == "Auto-download disabled"

    @pytest.mark.asyncio
    async def test_process_grab_event_webhook_disabled(
        self, temp_sonarr_handler, sample_grab_payload
    ):
        """Test processing Grab event when webhook is disabled"""
        temp_sonarr_handler.config.webhook_enabled = False

        payload = SonarrWebhookPayload(**sample_grab_payload)
        result = await temp_sonarr_handler.process_webhook(payload)

        assert result["status"] == "ignored"
        assert result["reason"] == "Webhook not enabled"

    @pytest.mark.asyncio
    async def test_process_test_event(self, temp_sonarr_handler):
        """Test processing Test event"""
        temp_sonarr_handler.config.webhook_enabled = True

        payload_data = {
            "eventType": "Test",
            "instanceName": "Sonarr",
            "applicationUrl": "http://localhost:8989"
        }
        payload = SonarrWebhookPayload(**payload_data)
        result = await temp_sonarr_handler.process_webhook(payload)

        assert result["status"] == "ok"
        assert result["message"] == "Test webhook received"


# ==================== UNIT TESTS ====================

@pytest.mark.unit
class TestSonarrUtilities:
    """Test Sonarr utility functions"""

    def test_get_sonarr_handler_singleton(self):
        """Test that get_sonarr_handler returns singleton instance"""
        handler1 = get_sonarr_handler()
        handler2 = get_sonarr_handler()
        assert handler1 is handler2


# ==================== SECURITY UTILITIES TESTS ====================

@pytest.mark.unit
class TestSecurityUtilities:
    """Test security utility functions"""

    def test_sanitize_filename_prevents_path_traversal(self):
        """Test that sanitize_filename prevents path traversal attacks"""
        from app.utils import sanitize_filename

        # Double dot attack - path separators replaced with underscores
        safe = sanitize_filename("../../../etc/passwd")
        assert "/" not in safe
        assert "\\" not in safe
        assert not safe.startswith("..")  # No leading double dots

        # Absolute path attack - slashes replaced
        safe = sanitize_filename("/etc/passwd")
        assert "/" not in safe
        assert safe == "_etc_passwd"

        # Windows path attack
        safe = sanitize_filename("C:\\Windows\\System32\\config")
        assert "C:" not in safe
        assert "\\" not in safe
        assert "Windows" in safe

    def test_sanitize_filename_removes_dangerous_characters(self):
        """Test that dangerous characters are removed"""
        from app.utils import sanitize_filename

        dangerous = "video<>:file?|.mp4"
        safe = sanitize_filename(dangerous)
        assert "<" not in safe
        assert ">" not in safe
        assert ":" not in safe
        assert "?" not in safe
        assert "|" not in safe
        assert "." in safe  # dots are ok in filename body
        assert "_" in safe  # underscores used as replacement

    def test_sanitize_filename_limits_length(self):
        """Test that filename length is limited"""
        from app.utils import sanitize_filename

        # Create very long filename
        long_name = "a" * 300
        safe = sanitize_filename(long_name)
        assert len(safe) <= 255

    def test_sanitize_filename_handles_empty_string(self):
        """Test that empty filename becomes 'download'"""
        from app.utils import sanitize_filename

        assert sanitize_filename("") == "download"
        assert sanitize_filename(None) == "download"

    def test_is_safe_filename_rejects_traversal(self):
        """Test that is_safe_filename rejects path traversal attempts"""
        from app.utils import is_safe_filename

        assert is_safe_filename("../../../etc/passwd") is False
        assert is_safe_filename("../test") is False
        assert is_safe_filename("./test") is False

    def test_is_safe_filename_rejects_absolute_paths(self):
        """Test that is_safe_filename rejects absolute paths"""
        from app.utils import is_safe_filename

        assert is_safe_filename("/etc/passwd") is False
        assert is_safe_filename("\\etc\\passwd") is False
        assert is_safe_filename("C:\\Windows\\System32\\config") is False

    def test_is_safe_filename_accepts_valid_names(self):
        """Test that is_safe_filename accepts valid filenames"""
        from app.utils import is_safe_filename

        assert is_safe_filename("video.mp4") is True
        assert is_safe_filename("test_file.mkv") is True
        assert is_safe_filename("anime_episode_01.mp4") is True

    def test_sanitize_filename_preserves_extension(self):
        """Test that file extension is preserved"""
        from app.utils import sanitize_filename

        assert sanitize_filename("video.mp4").endswith(".mp4")
        assert sanitize_filename("test.mkv").endswith(".mkv")
        assert sanitize_filename("anime.avi").endswith(".avi")
