//! Pilot: simulates user input (keyboard, mouse, focus) for headless UI tests.

use crossterm::event::{
    KeyCode, KeyEvent, KeyEventKind, KeyModifiers, MouseButton, MouseEvent, MouseEventKind,
};

use crate::event::AppEvent;

use super::TestApp;

/// Pilot simulates user input for headless integration tests.
///
/// Obtain a Pilot via [`TestApp::pilot()`]. Methods are async to support `settle()`.
///
/// # Example
/// ```ignore
/// let mut pilot = test_app.pilot();
/// pilot.press(KeyCode::Tab).await;
/// pilot.type_text("hello").await;
/// ```
pub struct Pilot<'a> {
    test_app: &'a mut TestApp,
}

impl<'a> Pilot<'a> {
    pub(super) fn new(test_app: &'a mut TestApp) -> Self {
        Pilot { test_app }
    }

    /// Simulate pressing a key (no modifiers).
    pub async fn press(&mut self, key: KeyCode) -> &mut Self {
        self.press_with_modifiers(key, KeyModifiers::NONE).await
    }

    /// Simulate pressing a key with the given modifier flags.
    pub async fn press_with_modifiers(
        &mut self,
        key: KeyCode,
        modifiers: KeyModifiers,
    ) -> &mut Self {
        let event = AppEvent::Key(KeyEvent {
            code: key,
            modifiers,
            kind: KeyEventKind::Press,
            state: crossterm::event::KeyEventState::NONE,
        });
        self.test_app.process_event(event);
        self.settle().await;
        self
    }

    /// Type a string by sending each character as an individual key press.
    pub async fn type_text(&mut self, text: &str) -> &mut Self {
        for ch in text.chars() {
            let event = AppEvent::Key(KeyEvent {
                code: KeyCode::Char(ch),
                modifiers: KeyModifiers::NONE,
                kind: KeyEventKind::Press,
                state: crossterm::event::KeyEventState::NONE,
            });
            self.test_app.process_event(event);
        }
        self.settle().await;
        self
    }

    /// Simulate a left mouse button click at (col, row).
    pub async fn click(&mut self, col: u16, row: u16) -> &mut Self {
        let event = AppEvent::Mouse(MouseEvent {
            kind: MouseEventKind::Down(MouseButton::Left),
            column: col,
            row,
            modifiers: KeyModifiers::NONE,
        });
        self.test_app.process_event(event);
        self.settle().await;
        self
    }

    /// Drain all pending events and reactive effects until quiescent.
    ///
    /// Yields to the tokio executor (allowing reactive effects to fire), then processes
    /// any events generated (e.g. `RenderRequest`). Repeats up to 100 times to prevent
    /// infinite loops. A final render is performed to capture the settled state.
    pub async fn settle(&mut self) {
        for _ in 0..100 {
            // Yield so reactive effects (spawn_local tasks) can run.
            tokio::task::yield_now().await;

            // Drain any events generated by reactive effects.
            let mut had_events = false;
            while let Ok(event) = self.test_app.rx.try_recv() {
                self.test_app.process_event(event);
                had_events = true;
            }

            // Drain message queue in case process_event posted new messages.
            self.test_app.app.drain_message_queue();

            // If nothing new to process, we have reached quiescence.
            if !had_events
                && self.test_app.rx.is_empty()
                && self.test_app.app.ctx().message_queue.borrow().is_empty()
            {
                break;
            }
        }

        // Final render to capture the settled state.
        self.test_app
            .app
            .render_to_terminal(&mut self.test_app.terminal)
            .expect("failed to render after settle");
    }
}
