"""Unit tests for MainWindow component."""

from unittest.mock import patch

import pytest
from PySide6.QtWidgets import QToolBar

from webdrop_bridge.config import Config
from webdrop_bridge.ui.main_window import MainWindow


@pytest.fixture
def sample_config(tmp_path):
    """Provide a test configuration."""
    # Create required directories
    allowed_root = tmp_path / "allowed"
    allowed_root.mkdir(exist_ok=True)

    # Create webapp HTML
    webapp_dir = tmp_path / "webapp"
    webapp_dir.mkdir(exist_ok=True)
    webapp_file = webapp_dir / "index.html"
    webapp_file.write_text("<html><body>Test</body></html>")

    config = Config(
        app_name="Test WebDrop",
        app_version="1.0.0",
        log_level="INFO",
        log_file=None,
        allowed_roots=[allowed_root],
        allowed_urls=["localhost", "127.0.0.1"],
        webapp_url=str(webapp_file),
        window_width=800,
        window_height=600,
        window_title="Test WebDrop v1.0.0",
        enable_logging=False,
    )
    return config


class TestMainWindowInitialization:
    """Test MainWindow initialization and setup."""

    def test_main_window_creation(self, qtbot, sample_config):
        """Test MainWindow can be instantiated."""
        window = MainWindow(sample_config)
        qtbot.addWidget(window)

        assert window is not None
        assert window.config == sample_config

    def test_main_window_title(self, qtbot, sample_config):
        """Test window title is set correctly."""
        window = MainWindow(sample_config)
        qtbot.addWidget(window)

        expected_title = f"{sample_config.app_name} v{sample_config.app_version}"
        assert window.windowTitle() == expected_title

    def test_main_window_geometry(self, qtbot, sample_config):
        """Test window geometry is set correctly."""
        window = MainWindow(sample_config)
        qtbot.addWidget(window)

        assert window.width() == sample_config.window_width
        assert window.height() == sample_config.window_height

    def test_main_window_has_web_view(self, qtbot, sample_config):
        """Test MainWindow has web_view attribute."""
        window = MainWindow(sample_config)
        qtbot.addWidget(window)

        assert hasattr(window, "web_view")
        assert window.web_view is not None

    def test_main_window_has_drag_interceptor(self, qtbot, sample_config):
        """Test MainWindow has drag_interceptor attribute."""
        window = MainWindow(sample_config)
        qtbot.addWidget(window)

        assert hasattr(window, "drag_interceptor")
        assert window.drag_interceptor is not None


class TestSettingsRestartBehavior:
    """Test restart prompts for settings changes that require a restart."""

    def test_branding_change_prompts_restart(self, qtbot, sample_config):
        """Changing the active branding should trigger the restart flow."""
        window = MainWindow(sample_config)
        qtbot.addWidget(window)

        with patch.object(window, "_handle_branding_change_restart") as mock_restart:
            with patch("webdrop_bridge.ui.settings_dialog.SettingsDialog") as mock_dialog_cls:
                mock_dialog = mock_dialog_cls.return_value
                mock_dialog.exec.side_effect = lambda: setattr(
                    window.config, "active_branding_id", "agravity"
                )
                window._show_settings_dialog()

        mock_restart.assert_called_once()


class TestMainWindowDragIntegration:
    """Test drag-and-drop integration."""

    def test_drag_interceptor_validator_set(self, qtbot, sample_config):
        """Test drag interceptor validator is configured."""
        window = MainWindow(sample_config)
        qtbot.addWidget(window)

        assert window.drag_interceptor._validator is not None

    def test_drag_interceptor_signals_connected(self, qtbot, sample_config):
        """Test drag interceptor signals are connected."""
        window = MainWindow(sample_config)
        qtbot.addWidget(window)

        # Signals should be connected
        assert window.drag_interceptor.drag_started is not None
        assert window.drag_interceptor.drag_failed is not None

    def test_handle_drag_delegates_to_interceptor(self, qtbot, sample_config, tmp_path):
        """Test drag handling delegates to interceptor."""
        from PySide6.QtCore import QCoreApplication

        window = MainWindow(sample_config)
        qtbot.addWidget(window)

        # Create test file
        test_file = sample_config.allowed_roots[0] / "test.txt"
        test_file.write_text("test")

        with patch.object(window.drag_interceptor, "handle_drag") as mock_drag:
            mock_drag.return_value = True
            # Call through bridge
            window._drag_bridge.start_file_drag(str(test_file))

            # Process deferred QTimer.singleShot(0, ...) call
            QCoreApplication.processEvents()

            mock_drag.assert_called_once_with(str(test_file))

    def test_on_drag_started_called(self, qtbot, sample_config):
        """Test _on_drag_started handler can be called."""
        window = MainWindow(sample_config)
        qtbot.addWidget(window)

        # Should not raise - new signature has source and local_path
        window._on_drag_started("https://example.com/file.png", "/local/path/file.png")

    def test_on_drag_failed_called(self, qtbot, sample_config):
        """Test _on_drag_failed handler can be called."""
        window = MainWindow(sample_config)
        qtbot.addWidget(window)

        # Should not raise - new signature has source and error
        window._on_drag_failed("https://example.com/file.png", "Test error message")


class TestMainWindowURLWhitelist:
    """Test URL whitelisting integration."""

    def test_restricted_web_view_receives_allowed_urls(self, qtbot, sample_config):
        """Test RestrictedWebEngineView receives allowed URLs from config."""
        window = MainWindow(sample_config)
        qtbot.addWidget(window)

        # web_view should have allowed_urls configured
        assert window.web_view.allowed_urls == sample_config.allowed_urls


class TestMainWindowOpenWith:
    """Test Open With chooser behavior."""

    def test_open_with_app_chooser_windows(self, qtbot, sample_config):
        """Windows should use ShellExecuteW with the openas verb."""
        window = MainWindow(sample_config)
        qtbot.addWidget(window)

        test_file = sample_config.allowed_roots[0] / "open_with_test.txt"
        test_file.write_text("test")

        with patch("webdrop_bridge.ui.main_window.sys.platform", "win32"):
            with patch("ctypes.windll.shell32.ShellExecuteW", return_value=33) as mock_shell:
                assert window._open_with_app_chooser(str(test_file)) is True
                mock_shell.assert_called_once_with(
                    None,
                    "openas",
                    str(test_file),
                    None,
                    None,
                    1,
                )

    def test_open_with_app_chooser_windows_shellexecute_failure(self, qtbot, sample_config):
        """Windows should fall back to OpenAs_RunDLL when ShellExecuteW fails."""
        window = MainWindow(sample_config)
        qtbot.addWidget(window)

        test_file = sample_config.allowed_roots[0] / "open_with_fallback.txt"
        test_file.write_text("test")

        with patch("webdrop_bridge.ui.main_window.sys.platform", "win32"):
            with patch("ctypes.windll.shell32.ShellExecuteW", return_value=31):
                with patch("webdrop_bridge.ui.main_window.subprocess.Popen") as mock_popen:
                    assert window._open_with_app_chooser(str(test_file)) is True
                    mock_popen.assert_called_once_with(
                        ["rundll32.exe", "shell32.dll,OpenAs_RunDLL", str(test_file)]
                    )

    def test_open_with_app_chooser_missing_file(self, qtbot, sample_config):
        """Missing files should fail before platform-specific invocation."""
        window = MainWindow(sample_config)
        qtbot.addWidget(window)

        with patch("webdrop_bridge.ui.main_window.sys.platform", "win32"):
            assert window._open_with_app_chooser("C:/tmp/does_not_exist.txt") is False

    def test_open_with_app_chooser_macos_success(self, qtbot, sample_config):
        """macOS should return True when osascript exits successfully."""
        window = MainWindow(sample_config)
        qtbot.addWidget(window)

        test_file = sample_config.allowed_roots[0] / "open_with_macos.txt"
        test_file.write_text("test")

        call_count = [0]  # Use list to make it mutable in nested function

        class _AppChooseResult:
            returncode = 0
            stdout = "TextEdit"  # Simulated chosen app name

        class _OpenResult:
            returncode = 0
            stdout = ""

        def mock_run(*args, **kwargs):
            """Mock subprocess.run with two different behaviors per call."""
            call_count[0] += 1
            # First call: osascript to choose application
            if call_count[0] == 1:
                return _AppChooseResult()
            # Second call: open command to open the file
            elif call_count[0] == 2:
                return _OpenResult()
            else:
                raise AssertionError(f"Unexpected call #{call_count[0]} to subprocess.run")

        with patch("webdrop_bridge.ui.main_window.sys.platform", "darwin"):
            with patch("webdrop_bridge.ui.main_window.subprocess.run", side_effect=mock_run):
                assert window._open_with_app_chooser(str(test_file)) is True

    def test_open_with_app_chooser_unsupported_platform(self, qtbot, sample_config):
        """Unsupported platforms should return False."""
        window = MainWindow(sample_config)
        qtbot.addWidget(window)

        with patch("webdrop_bridge.ui.main_window.sys.platform", "linux"):
            assert window._open_with_app_chooser("/tmp/test.txt") is False
