"""Integration tests for signal/terminal protection.

These tests verify the editor survives hostile keystrokes that would
normally kill or suspend a process (Ctrl-C, Ctrl-Z, Ctrl-\, Ctrl-Y).
"""

import os
import pty
import signal
import time
import select

import pytest


def wait_for_output(fd, timeout=2.0):
    """Wait for and read output from PTY."""
    output = b""
    end_time = time.time() + timeout
    while time.time() < end_time:
        ready, _, _ = select.select([fd], [], [], 0.1)
        if ready:
            try:
                chunk = os.read(fd, 4096)
                if chunk:
                    output += chunk
            except OSError:
                break
    return output


def process_alive(pid):
    """Check if process is still running."""
    result = os.waitpid(pid, os.WNOHANG)
    return result == (0, 0)


class TestEditorSurvivesSignals:
    """Test that editor survives terminal control characters."""

    @pytest.fixture
    def editor_process(self):
        """Spawn editor in a PTY and yield (pid, fd)."""
        pid, fd = pty.fork()
        if pid == 0:
            # Child process - run the editor
            os.execvp('python', ['python', '-m', 'e3po'])
        else:
            # Parent - give editor time to initialize
            time.sleep(0.5)
            yield pid, fd
            # Cleanup - kill the editor
            try:
                os.kill(pid, signal.SIGTERM)
                os.waitpid(pid, 0)
            except (OSError, ChildProcessError):
                pass
            try:
                os.close(fd)
            except OSError:
                pass

    def test_survives_ctrl_c(self, editor_process):
        """Editor should survive Ctrl-C (SIGINT)."""
        pid, fd = editor_process
        assert process_alive(pid), "Editor not running before test"

        # Send Ctrl-C
        os.write(fd, b'\x03')
        time.sleep(0.2)

        assert process_alive(pid), "Editor died from Ctrl-C!"

    def test_survives_ctrl_z(self, editor_process):
        """Editor should survive Ctrl-Z (SIGTSTP)."""
        pid, fd = editor_process
        assert process_alive(pid), "Editor not running before test"

        # Send Ctrl-Z
        os.write(fd, b'\x1a')
        time.sleep(0.2)

        assert process_alive(pid), "Editor died from Ctrl-Z!"

    def test_survives_ctrl_backslash(self, editor_process):
        """Editor should survive Ctrl-\\ (SIGQUIT)."""
        pid, fd = editor_process
        assert process_alive(pid), "Editor not running before test"

        # Send Ctrl-\
        os.write(fd, b'\x1c')
        time.sleep(0.2)

        assert process_alive(pid), "Editor died from Ctrl-\\!"

    def test_survives_ctrl_y(self, editor_process):
        """Editor should survive Ctrl-Y (DSUSP)."""
        pid, fd = editor_process
        assert process_alive(pid), "Editor not running before test"

        # Send Ctrl-Y
        os.write(fd, b'\x19')
        time.sleep(0.2)

        assert process_alive(pid), "Editor died from Ctrl-Y!"

    def test_survives_all_hostile_keys(self, editor_process):
        """Editor should survive rapid-fire hostile keystrokes."""
        pid, fd = editor_process
        assert process_alive(pid), "Editor not running before test"

        # Send all hostile keys in rapid succession
        hostile_keys = [
            b'\x03',  # Ctrl-C
            b'\x1a',  # Ctrl-Z
            b'\x1c',  # Ctrl-\
            b'\x19',  # Ctrl-Y
            b'\x13',  # Ctrl-S (flow control)
            b'\x11',  # Ctrl-Q (flow control)
        ]

        for key in hostile_keys:
            os.write(fd, key)
            time.sleep(0.05)

        time.sleep(0.2)
        assert process_alive(pid), "Editor died from hostile keystrokes!"

    def test_editor_responds_after_hostile_keys(self, editor_process):
        """Editor should still respond to input after hostile keys."""
        pid, fd = editor_process

        # Send hostile keys
        os.write(fd, b'\x03\x1a\x1c\x19')
        time.sleep(0.2)

        assert process_alive(pid), "Editor died from hostile keystrokes!"

        # Send normal text - editor should still work
        os.write(fd, b'hello')
        time.sleep(0.2)

        assert process_alive(pid), "Editor died after typing!"

        # Try to quit gracefully with ESC q
        os.write(fd, b'\x1bq\r')  # ESC, q, Enter
        time.sleep(0.3)
