"""
Integration tests for remote control functionality.

Tests the remote control system including Unix socket and TCP communication,
command parsing, and settings management.
"""

import pytest
import socket
import time
from unittest.mock import Mock, patch

from fenrirscreenreader.core.remoteManager import RemoteManager


@pytest.mark.integration
@pytest.mark.remote
class TestRemoteCommandParsing:
    """Test remote control command parsing and execution."""

    def setup_method(self):
        """Create RemoteManager instance for each test."""
        self.manager = RemoteManager()

    def test_say_command_parsing(self, mock_environment):
        """Test parsing of 'command say' messages."""
        self.manager.initialize(mock_environment)

        result = self.manager.handle_command_execution_with_response(
            "say Hello World"
        )

        assert result["success"] is True
        assert "Speaking" in result["message"]
        mock_environment["runtime"]["OutputManager"].speak_text.assert_called_once_with(
            "Hello World"
        )

    def test_interrupt_command(self, mock_environment):
        """Test speech interruption command."""
        self.manager.initialize(mock_environment)

        result = self.manager.handle_command_execution_with_response("interrupt")

        assert result["success"] is True
        mock_environment["runtime"][
            "OutputManager"
        ].interrupt_output.assert_called_once()

    def test_setting_change_parsing(self, mock_environment):
        """Test parsing of 'setting set' commands."""
        self.manager.initialize(mock_environment)

        # Mock parse_setting_args to verify it gets called
        with patch.object(
            mock_environment["runtime"]["SettingsManager"], "parse_setting_args"
        ) as mock_parse:
            result = self.manager.handle_settings_change_with_response(
                "set speech#rate=0.8"
            )

            assert result["success"] is True
            mock_parse.assert_called_once_with("speech#rate=0.8")

    def test_clipboard_command(self, mock_environment):
        """Test clipboard setting via remote control."""
        self.manager.initialize(mock_environment)

        result = self.manager.handle_command_execution_with_response(
            "clipboard Test clipboard content"
        )

        assert result["success"] is True
        mock_environment["runtime"][
            "MemoryManager"
        ].add_value_to_first_index.assert_called_once_with(
            "clipboardHistory", "Test clipboard content"
        )

    def test_quit_command(self, mock_environment):
        """Test Fenrir shutdown command."""
        self.manager.initialize(mock_environment)

        result = self.manager.handle_command_execution_with_response(
            "quitapplication"
        )

        assert result["success"] is True
        mock_environment["runtime"][
            "EventManager"
        ].stop_main_event_loop.assert_called_once()

    def test_unknown_command_rejection(self, mock_environment):
        """Test that unknown commands are rejected."""
        self.manager.initialize(mock_environment)

        result = self.manager.handle_command_execution_with_response(
            "unknown_command"
        )

        assert result["success"] is False
        assert "Unknown command" in result["message"]


@pytest.mark.integration
@pytest.mark.remote
class TestRemoteSettingsControl:
    """Test remote control of settings."""

    def setup_method(self):
        """Create RemoteManager instance for each test."""
        self.manager = RemoteManager()

    def test_setting_reset(self, mock_environment):
        """Test resetting settings to defaults."""
        self.manager.initialize(mock_environment)

        with patch.object(
            mock_environment["runtime"]["SettingsManager"], "reset_setting_arg_dict"
        ) as mock_reset:
            result = self.manager.handle_settings_change_with_response("reset")

            assert result["success"] is True
            mock_reset.assert_called_once()

    def test_setting_save(self, mock_environment):
        """Test saving settings to file."""
        self.manager.initialize(mock_environment)

        mock_environment["runtime"]["SettingsManager"].get_settings_file = Mock(
            return_value="/tmp/test.conf"
        )

        with patch.object(
            mock_environment["runtime"]["SettingsManager"], "save_settings"
        ) as mock_save:
            result = self.manager.handle_settings_change_with_response("save")

            assert result["success"] is True
            mock_save.assert_called_once()

    def test_settings_remote_disabled(self, mock_environment):
        """Test that settings commands are blocked when disabled."""
        mock_environment["runtime"]["SettingsManager"].get_setting_as_bool = Mock(
            return_value=False
        )
        self.manager.initialize(mock_environment)

        result = self.manager.handle_settings_change_with_response(
            "set speech#rate=0.5"
        )

        assert result["success"] is False
        assert "disabled" in result["message"].lower()


@pytest.mark.integration
@pytest.mark.remote
class TestRemoteDataFormat:
    """Test remote control data format handling."""

    def setup_method(self):
        """Create RemoteManager instance for each test."""
        self.manager = RemoteManager()

    def test_command_prefix_case_insensitive(self, mock_environment):
        """Test that command prefixes are case-insensitive."""
        self.manager.initialize(mock_environment)

        # All of these should work
        result1 = self.manager.handle_remote_incomming_with_response(
            "COMMAND say test"
        )
        result2 = self.manager.handle_remote_incomming_with_response(
            "command say test"
        )
        result3 = self.manager.handle_remote_incomming_with_response(
            "CoMmAnD say test"
        )

        assert all(r["success"] for r in [result1, result2, result3])

    def test_setting_prefix_case_insensitive(self, mock_environment):
        """Test that setting prefixes are case-insensitive."""
        self.manager.initialize(mock_environment)

        with patch.object(
            mock_environment["runtime"]["SettingsManager"], "parse_setting_args"
        ):
            result1 = self.manager.handle_remote_incomming_with_response(
                "SETTING set speech#rate=0.5"
            )
            result2 = self.manager.handle_remote_incomming_with_response(
                "setting set speech#rate=0.5"
            )

            assert all(r["success"] for r in [result1, result2])

    def test_empty_data_handling(self, mock_environment):
        """Test handling of empty remote data."""
        self.manager.initialize(mock_environment)

        result = self.manager.handle_remote_incomming_with_response("")

        assert result["success"] is False
        assert "No data" in result["message"]

    def test_invalid_format_rejection(self, mock_environment):
        """Test rejection of invalid command format."""
        self.manager.initialize(mock_environment)

        result = self.manager.handle_remote_incomming_with_response(
            "invalid format without prefix"
        )

        assert result["success"] is False
        assert "Unknown command format" in result["message"]

    def test_list_instances_top_level_command(self, mock_environment):
        """Test listing registered Fenrir instances."""
        self.manager.initialize(mock_environment)

        with patch(
            "fenrirscreenreader.core.remoteManager.remoteInstanceRegistry.list_instances",
            return_value=[
                {
                    "pid": 123,
                    "ppid": 100,
                    "screen_driver": "ptyDriver",
                    "keyboard_driver": "x11Driver",
                    "main_socket": True,
                    "x11_window_id": "0x123",
                    "socket_files": [
                        "/tmp/fenrirscreenreader-123.sock",
                        "/tmp/fenrirscreenreader-deamon.sock",
                    ],
                }
            ],
        ):
            result = self.manager.handle_remote_incomming_with_response("ls")

        assert result["success"] is True
        assert "pid=123" in result["message"]
        assert "x11_window_id=0x123" in result["message"]
        assert "/tmp/fenrirscreenreader-123.sock" in result["message"]

    def test_remote_incoming_suppresses_command_claimed_by_other_instance(
        self, mock_environment, tmp_path
    ):
        """Test untargeted duplicate remote commands only run in one process."""
        self.manager.initialize(mock_environment)
        lock_path = tmp_path / "remote-command.lock"
        event_data = "command say duplicated"
        event_hash = self.manager._get_remote_command_hash(event_data)
        lock_path.write_text(f"999999 {event_hash} {time.time()}\n")

        with patch.object(
            self.manager,
            "_get_remote_command_lock_path",
            return_value=str(lock_path),
        ):
            self.manager.handle_remote_incomming(event_data)

        mock_environment["runtime"]["OutputManager"].speak_text.assert_not_called()

    def test_remote_incoming_allows_same_instance_repeat(
        self, mock_environment, tmp_path
    ):
        """Test repeated direct commands to one instance are not suppressed."""
        self.manager.initialize(mock_environment)
        lock_path = tmp_path / "remote-command.lock"

        with patch.object(
            self.manager,
            "_get_remote_command_lock_path",
            return_value=str(lock_path),
        ):
            self.manager.handle_remote_incomming("command say repeat")
            self.manager.handle_remote_incomming("command say repeat")

        assert mock_environment["runtime"]["OutputManager"].speak_text.call_count == 2

    def test_remote_incoming_allows_different_command_after_claim(
        self, mock_environment, tmp_path
    ):
        """Test a fresh different command is not suppressed by the shared lock."""
        self.manager.initialize(mock_environment)
        lock_path = tmp_path / "remote-command.lock"
        previous_hash = self.manager._get_remote_command_hash("command say first")
        lock_path.write_text(f"999999 {previous_hash} {time.time()}\n")

        with patch.object(
            self.manager,
            "_get_remote_command_lock_path",
            return_value=str(lock_path),
        ):
            self.manager.handle_remote_incomming("command say second")

        mock_environment["runtime"]["OutputManager"].speak_text.assert_called_once()


@pytest.mark.integration
@pytest.mark.remote
class TestWindowDefinition:
    """Test window definition via remote control."""

    def setup_method(self):
        """Create RemoteManager instance for each test."""
        self.manager = RemoteManager()

    def test_define_window_valid_coordinates(self, mock_environment):
        """Test defining a window with valid coordinates."""
        self.manager.initialize(mock_environment)

        result = self.manager.handle_command_execution_with_response(
            "window 10 5 70 20"
        )

        assert result["success"] is True
        mock_environment["runtime"][
            "CursorManager"
        ].set_window_for_application.assert_called_once()

        # Verify the coordinates were parsed correctly
        call_args = mock_environment["runtime"][
            "CursorManager"
        ].set_window_for_application.call_args
        start, end = call_args[0]
        assert start == {"x": 10, "y": 5}
        assert end == {"x": 70, "y": 20}

    def test_define_window_insufficient_coordinates(self, mock_environment):
        """Test that window definition with < 4 coordinates is ignored."""
        self.manager.initialize(mock_environment)

        # Should succeed but not call set_window_for_application
        result = self.manager.handle_command_execution_with_response("window 10 20")

        assert result["success"] is True
        mock_environment["runtime"][
            "CursorManager"
        ].set_window_for_application.assert_not_called()

    def test_reset_window(self, mock_environment):
        """Test resetting window to full screen."""
        self.manager.initialize(mock_environment)

        result = self.manager.handle_command_execution_with_response("resetwindow")

        assert result["success"] is True
        mock_environment["runtime"][
            "CursorManager"
        ].clear_window_for_application.assert_called_once()


@pytest.mark.integration
@pytest.mark.remote
class TestVMenuControl:
    """Test VMenu control via remote."""

    def setup_method(self):
        """Create RemoteManager instance for each test."""
        self.manager = RemoteManager()

    def test_set_vmenu(self, mock_environment):
        """Test setting VMenu to specific menu."""
        self.manager.initialize(mock_environment)

        result = self.manager.handle_command_execution_with_response(
            "vmenu /vim/file/save"
        )

        assert result["success"] is True
        mock_environment["runtime"]["VmenuManager"].set_curr_menu.assert_called_once_with(
            "/vim/file/save"
        )

    def test_reset_vmenu(self, mock_environment):
        """Test resetting VMenu to default."""
        self.manager.initialize(mock_environment)

        result = self.manager.handle_command_execution_with_response("resetvmenu")

        assert result["success"] is True
        mock_environment["runtime"]["VmenuManager"].set_curr_menu.assert_called_once_with()


@pytest.mark.integration
@pytest.mark.remote
@pytest.mark.slow
class TestRemoteControlThroughput:
    """Test remote control performance characteristics."""

    def test_rapid_say_commands(self, mock_environment):
        """Test handling of rapid successive say commands."""
        manager = RemoteManager()
        manager.initialize(mock_environment)

        # Send 100 rapid commands
        for i in range(100):
            result = manager.handle_command_execution_with_response(f"say test {i}")
            assert result["success"] is True

        # Verify all were queued
        assert (
            mock_environment["runtime"]["OutputManager"].speak_text.call_count == 100
        )

    def test_rapid_setting_changes(self, mock_environment):
        """Test handling of rapid setting changes."""
        manager = RemoteManager()
        manager.initialize(mock_environment)

        # Rapidly change speech rate
        with patch.object(
            mock_environment["runtime"]["SettingsManager"], "parse_setting_args"
        ) as mock_parse:
            for rate in [0.5, 0.6, 0.7, 0.8, 0.9, 1.0]:
                result = manager.handle_settings_change_with_response(
                    f"set speech#rate={rate}"
                )
                assert result["success"] is True

            assert mock_parse.call_count == 6
