"""Unit tests for DragInterceptor component."""

from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

from webdrop_bridge.config import Config
from webdrop_bridge.core.drag_interceptor import DragInterceptor


@pytest.fixture
def test_config(tmp_path):
    """Create test configuration."""
    return Config(
        app_name="Test App",
        app_version="1.0.0",
        log_level="INFO",
        log_file=None,
        allowed_roots=[tmp_path],
        allowed_urls=[],
        webapp_url="https://dev.agravity.io/",
        url_mappings=[],
        check_file_exists=True,
    )


class TestDragInterceptorInitialization:
    """Test DragInterceptor initialization and setup."""

    def test_drag_interceptor_creation(self, qtbot, test_config):
        """Test DragInterceptor can be instantiated."""
        interceptor = DragInterceptor(test_config)
        assert interceptor is not None
        assert interceptor._validator is not None
        assert interceptor._url_converter is not None

    def test_drag_interceptor_has_signals(self, qtbot, test_config):
        """Test DragInterceptor has required signals."""
        interceptor = DragInterceptor(test_config)
        assert hasattr(interceptor, "drag_started")
        assert hasattr(interceptor, "drag_failed")

    def test_set_validator(self, qtbot, test_config):
        """Test validator is set during construction."""
        interceptor = DragInterceptor(test_config)
        assert interceptor._validator is not None


class TestDragInterceptorValidation:
    """Test path validation in drag operations."""

    def test_handle_drag_empty_text(self, qtbot, test_config):
        """Test handling drag with empty text fails."""
        interceptor = DragInterceptor(test_config)
        with qtbot.waitSignal(interceptor.drag_failed):
            result = interceptor.handle_drag("")

        assert result is False

    def test_handle_drag_valid_file_path(self, qtbot, tmp_path):
        """Test handling drag with valid file path."""
        # Create a test file
        test_file = tmp_path / "test.txt"
        test_file.write_text("test content")

        config = Config(
            app_name="Test",
            app_version="1.0.0",
            log_level="INFO",
            log_file=None,
            allowed_roots=[tmp_path],
            allowed_urls=[],
            webapp_url="https://test.com/",
            url_mappings=[],
            check_file_exists=True,
        )
        interceptor = DragInterceptor(config)

        # Mock the drag operation to simulate success
        with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
            mock_drag_instance = MagicMock()
            # Simulate successful copy action
            from PySide6.QtCore import Qt

            mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
            mock_drag.return_value = mock_drag_instance

            result = interceptor.handle_drag(str(test_file))

        # Should return True on successful drag
        assert result is True

    def test_handle_drag_invalid_path(self, qtbot, test_config):
        """Test drag with invalid path fails."""
        interceptor = DragInterceptor(test_config)

        # Path outside allowed roots
        invalid_path = "/etc/passwd"

        with qtbot.waitSignal(interceptor.drag_failed):
            result = interceptor.handle_drag(invalid_path)

        assert result is False

    def test_handle_drag_nonexistent_file(self, qtbot, test_config, tmp_path):
        """Test drag with nonexistent file fails."""
        interceptor = DragInterceptor(test_config)

        nonexistent = tmp_path / "nonexistent.txt"

        with qtbot.waitSignal(interceptor.drag_failed):
            result = interceptor.handle_drag(str(nonexistent))

        assert result is False


class TestDragInterceptorAzureURL:
    """Test Azure URL to local path conversion in drag operations."""

    def test_handle_drag_azure_url(self, qtbot, tmp_path):
        """Test handling drag with Azure Blob Storage URL."""
        from webdrop_bridge.config import URLMapping

        # Create test file that would be the result
        test_file = tmp_path / "test.png"
        test_file.write_text("image data")

        config = Config(
            app_name="Test",
            app_version="1.0.0",
            log_level="INFO",
            log_file=None,
            allowed_roots=[tmp_path],
            allowed_urls=[],
            webapp_url="https://test.com/",
            url_mappings=[
                URLMapping(
                    url_prefix="https://devagravitystg.file.core.windows.net/devagravitysync/",
                    local_path=str(tmp_path),
                )
            ],
            check_file_exists=True,
        )
        interceptor = DragInterceptor(config)

        # Azure URL
        azure_url = "https://devagravitystg.file.core.windows.net/devagravitysync/test.png"

        # Mock the drag operation
        with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
            mock_drag_instance = MagicMock()
            from PySide6.QtCore import Qt

            mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
            mock_drag.return_value = mock_drag_instance

            result = interceptor.handle_drag(azure_url)

        assert result is True

    def test_handle_drag_unmapped_url(self, qtbot, test_config):
        """Test handling drag with unmapped URL fails."""
        interceptor = DragInterceptor(test_config)

        # URL with no mapping
        unmapped_url = "https://unknown.blob.core.windows.net/container/file.png"

        with qtbot.waitSignal(interceptor.drag_failed):
            result = interceptor.handle_drag(unmapped_url)

        assert result is False


class TestDragInterceptorSignals:
    """Test signal emission on drag operations."""

    def test_drag_started_signal_emitted(self, qtbot, tmp_path):
        """Test drag_started signal is emitted on success."""
        test_file = tmp_path / "test.txt"
        test_file.write_text("content")

        config = Config(
            app_name="Test",
            app_version="1.0.0",
            log_level="INFO",
            log_file=None,
            allowed_roots=[tmp_path],
            allowed_urls=[],
            webapp_url="https://test.com/",
            url_mappings=[],
            check_file_exists=True,
        )
        interceptor = DragInterceptor(config)

        # Connect to signal manually
        signal_spy = []
        interceptor.drag_started.connect(lambda src, path: signal_spy.append((src, path)))

        from PySide6.QtCore import Qt

        with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
            mock_drag_instance = MagicMock()
            mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
            mock_drag.return_value = mock_drag_instance

            result = interceptor.handle_drag(str(test_file))

        # Verify result and signal emission
        assert result is True
        assert len(signal_spy) == 1

    def test_drag_failed_signal_on_empty_text(self, qtbot, test_config):
        """Test drag_failed signal on empty text."""
        interceptor = DragInterceptor(test_config)

        # Connect to signal manually
        signal_spy = []
        interceptor.drag_failed.connect(lambda src, msg: signal_spy.append((src, msg)))

        result = interceptor.handle_drag("")

        # Verify result and signal emission
        assert result is False
        assert len(signal_spy) == 1
        assert "Empty" in signal_spy[0][1]

    def test_drag_failed_signal_on_validation_error(self, qtbot, test_config):
        """Test drag_failed signal on validation failure."""
        interceptor = DragInterceptor(test_config)

        # Connect to signal manually
        signal_spy = []
        interceptor.drag_failed.connect(lambda src, msg: signal_spy.append((src, msg)))

        result = interceptor.handle_drag("/invalid/path/file.txt")

        # Verify result and signal emission
        assert result is False
        assert len(signal_spy) == 1


class TestDragInterceptorMultipleDrags:
    """Test multiple file drag support."""

    def test_handle_drag_with_list_single_item(self, qtbot, tmp_path):
        """Test handle_drag with list containing single file path."""
        test_file = tmp_path / "test.txt"
        test_file.write_text("content")

        config = Config(
            app_name="Test",
            app_version="1.0.0",
            log_level="INFO",
            log_file=None,
            allowed_roots=[tmp_path],
            allowed_urls=[],
            webapp_url="https://test.com/",
            url_mappings=[],
            check_file_exists=True,
        )
        interceptor = DragInterceptor(config)

        from PySide6.QtCore import Qt

        with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
            mock_drag_instance = MagicMock()
            mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
            mock_drag.return_value = mock_drag_instance

            result = interceptor.handle_drag([str(test_file)])

        assert result is True

    def test_handle_drag_with_multiple_files(self, qtbot, tmp_path):
        """Test handle_drag with list of multiple file paths."""
        # Create multiple test files
        test_file1 = tmp_path / "test1.txt"
        test_file1.write_text("content1")
        test_file2 = tmp_path / "test2.txt"
        test_file2.write_text("content2")
        test_file3 = tmp_path / "test3.txt"
        test_file3.write_text("content3")

        config = Config(
            app_name="Test",
            app_version="1.0.0",
            log_level="INFO",
            log_file=None,
            allowed_roots=[tmp_path],
            allowed_urls=[],
            webapp_url="https://test.com/",
            url_mappings=[],
            check_file_exists=True,
        )
        interceptor = DragInterceptor(config)

        from PySide6.QtCore import Qt

        with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
            mock_drag_instance = MagicMock()
            mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
            mock_drag.return_value = mock_drag_instance

            result = interceptor.handle_drag(
                [
                    str(test_file1),
                    str(test_file2),
                    str(test_file3),
                ]
            )

        assert result is True

    def test_handle_drag_with_multiple_azure_urls(self, qtbot, tmp_path):
        """Test handle_drag with list of multiple Azure URLs."""
        from webdrop_bridge.config import URLMapping

        config = Config(
            app_name="Test",
            app_version="1.0.0",
            log_level="INFO",
            log_file=None,
            allowed_roots=[tmp_path],
            allowed_urls=[],
            webapp_url="https://test.com/",
            url_mappings=[
                URLMapping(
                    url_prefix="https://produktagravitystg.file.core.windows.net/produktagravitysync/",
                    local_path=str(tmp_path),
                )
            ],
            check_file_exists=False,  # Don't check file existence for this test
        )
        interceptor = DragInterceptor(config)

        # Multiple Azure URLs (as would be in a multi-drag)
        azure_urls = [
            "https://produktagravitystg.file.core.windows.net/produktagravitysync/axtZdPVjs5iUaKU2muKMFN1WZ/igkjieyjcko.jpg",
            "https://produktagravitystg.file.core.windows.net/produktagravitysync/aWd7mDjnsm2w0PHU9AryQBYz2/457101023fd46d673e2ce6642f78fb0d62736f0f06c7.jpg",
        ]

        from PySide6.QtCore import Qt

        with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
            mock_drag_instance = MagicMock()
            mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
            mock_drag.return_value = mock_drag_instance

            result = interceptor.handle_drag(azure_urls)

        assert result is True
        # Verify QDrag.exec was called (meaning drag was set up correctly)
        mock_drag_instance.exec.assert_called_once()

    def test_handle_drag_mixed_urls_and_paths(self, qtbot, tmp_path):
        """Test handle_drag with mixed Azure URLs and local paths."""
        from webdrop_bridge.config import URLMapping

        # Create test file
        test_file = tmp_path / "local_file.txt"
        test_file.write_text("local content")

        config = Config(
            app_name="Test",
            app_version="1.0.0",
            log_level="INFO",
            log_file=None,
            allowed_roots=[tmp_path],
            allowed_urls=[],
            webapp_url="https://test.com/",
            url_mappings=[
                URLMapping(
                    url_prefix="https://devagravitystg.file.core.windows.net/devagravitysync/",
                    local_path=str(tmp_path),
                )
            ],
            check_file_exists=False,  # Don't check existence for remote files
        )
        interceptor = DragInterceptor(config)

        mixed_items = [
            str(test_file),  # local path
            "https://devagravitystg.file.core.windows.net/devagravitysync/remote.jpg",  # Azure URL
        ]

        from PySide6.QtCore import Qt

        with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
            mock_drag_instance = MagicMock()
            mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
            mock_drag.return_value = mock_drag_instance

            result = interceptor.handle_drag(mixed_items)

        assert result is True

    def test_handle_drag_multiple_empty_list(self, qtbot, test_config):
        """Test handle_drag with empty list fails."""
        interceptor = DragInterceptor(test_config)

        with qtbot.waitSignal(interceptor.drag_failed):
            result = interceptor.handle_drag([])

        assert result is False

    def test_handle_drag_multiple_one_invalid_fails(self, qtbot, tmp_path):
        """Test handle_drag with multiple files fails if one is invalid."""
        test_file1 = tmp_path / "test1.txt"
        test_file1.write_text("content1")

        config = Config(
            app_name="Test",
            app_version="1.0.0",
            log_level="INFO",
            log_file=None,
            allowed_roots=[tmp_path],
            allowed_urls=[],
            webapp_url="https://test.com/",
            url_mappings=[],
            check_file_exists=True,
        )
        interceptor = DragInterceptor(config)

        # One valid, one invalid
        files = [
            str(test_file1),
            "/etc/passwd",  # Invalid - outside allowed roots
        ]

        with qtbot.waitSignal(interceptor.drag_failed):
            result = interceptor.handle_drag(files)

        assert result is False

    def test_handle_drag_multiple_signal_with_pipes(self, qtbot, tmp_path):
        """Test drag_started signal contains pipe-separated paths for multiple files."""
        test_file1 = tmp_path / "test1.txt"
        test_file1.write_text("content1")
        test_file2 = tmp_path / "test2.txt"
        test_file2.write_text("content2")

        config = Config(
            app_name="Test",
            app_version="1.0.0",
            log_level="INFO",
            log_file=None,
            allowed_roots=[tmp_path],
            allowed_urls=[],
            webapp_url="https://test.com/",
            url_mappings=[],
            check_file_exists=True,
        )
        interceptor = DragInterceptor(config)

        signal_spy = []
        interceptor.drag_started.connect(lambda src, path: signal_spy.append((src, path)))

        from PySide6.QtCore import Qt

        with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
            mock_drag_instance = MagicMock()
            mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
            mock_drag.return_value = mock_drag_instance

            result = interceptor.handle_drag([str(test_file1), str(test_file2)])

        assert result is True
        assert len(signal_spy) == 1
        # Multiple paths should be separated by " | "
        assert " | " in signal_spy[0][1]
