/// Integration tests for TextArea widget (RFC 57).
use lv_tui::prelude::*;
use lv_tui::widgets::TextArea;

fn has_cell(buf: &Buffer, text: &str) -> bool {
    let s: String = buf.cells.iter().map(|c| c.symbol.as_str()).collect();
    s.contains(text)
}

// ── Basic rendering ────────────────────────────────────────────

#[test]
fn textarea_renders_initial_text() {
    let mut pilot = Pilot::new(
        TextArea::new().text("hello\nworld").show_line_numbers(true),
        30, 8,
    );
    pilot.focus_first();
    assert!(has_cell(pilot.frame(), "hello"), "should render first line");
    assert!(has_cell(pilot.frame(), "world"), "should render second line");
}

#[test]
fn textarea_shows_line_numbers() {
    let pilot = Pilot::new(
        TextArea::new().text("line1\nline2").show_line_numbers(true),
        30, 8,
    );
    assert!(has_cell(pilot.frame(), "1"), "should show line number 1");
    assert!(has_cell(pilot.frame(), "2"), "should show line number 2");
}

// ── Character insertion ────────────────────────────────────────

#[test]
fn textarea_types_characters() {
    let mut pilot = Pilot::new(
        TextArea::new().show_line_numbers(true),
        30, 8,
    );
    pilot.focus_first();

    pilot.press(Key::Char('a')).unwrap();
    pilot.press(Key::Char('b')).unwrap();
    assert!(has_cell(pilot.frame(), "ab"), "should show typed text");
}

// ── Backspace ───────────────────────────────────────────────────

#[test]
fn textarea_backspace_deletes() {
    let mut pilot = Pilot::new(
        TextArea::new().text("abc").show_line_numbers(true),
        30, 8,
    );
    pilot.focus_first();

    // Cursor starts at (0,3) — last char index of first line
    pilot.press(Key::End).unwrap();     // move to end of first line
    pilot.press(Key::Backspace).unwrap();
    pilot.press(Key::Backspace).unwrap();
    assert!(has_cell(pilot.frame(), "a"), "backspace should delete chars");
    assert!(!has_cell(pilot.frame(), "bc"), "deleted chars should be gone");
}

// ── Delete ──────────────────────────────────────────────────────

#[test]
fn textarea_delete_removes_next_char() {
    let mut pilot = Pilot::new(
        TextArea::new().text("xyz").show_line_numbers(true),
        30, 8,
    );
    pilot.focus_first();

    // Cursor starts at (0,0) — beginning of first line
    pilot.press(Key::Home).unwrap();    // ensure at start
    pilot.press(Key::Delete).unwrap();
    assert!(!has_cell(pilot.frame(), "x"), "delete should remove first char");
    assert!(has_cell(pilot.frame(), "yz"), "remaining chars should be visible");
}

// ── Enter splits line ──────────────────────────────────────────

#[test]
fn textarea_enter_splits_line() {
    let mut pilot = Pilot::new(
        TextArea::new().text("hello").show_line_numbers(true),
        30, 8,
    );
    pilot.focus_first();

    // Move cursor to position 2 (between "he" and "llo")
    // Cursor starts at (0,0). Press Right twice to get to col 2
    pilot.press(Key::Right).unwrap();
    pilot.press(Key::Right).unwrap();
    pilot.press(Key::Enter).unwrap();

    assert!(has_cell(pilot.frame(), "he"), "first part before split");
    assert!(has_cell(pilot.frame(), "llo"), "second part after split");
}

// ── Arrow navigation ────────────────────────────────────────────

#[test]
fn textarea_arrows_navigate() {
    let mut pilot = Pilot::new(
        TextArea::new().text("aaa\nbbb").show_line_numbers(true),
        30, 8,
    );
    pilot.focus_first();
    // Cursor at (0,0). Down to second line
    pilot.press(Key::Down).unwrap();
    // Cursor now at (1,0). Type to replace/insert
    pilot.press(Key::Right).unwrap();
    assert!(has_cell(pilot.frame(), "bbb"), "second line still rendered");
}

// ── Home / End ──────────────────────────────────────────────────

#[test]
fn textarea_home_end_navigation() {
    let mut pilot = Pilot::new(
        TextArea::new().text("long line of text").show_line_numbers(true),
        30, 8,
    );
    pilot.focus_first();
    // Home: cursor to col 0
    pilot.press(Key::Home).unwrap();
    // Type at start
    pilot.press(Key::Char('X')).unwrap();
    assert!(has_cell(pilot.frame(), "Xlong"), "char inserted at start");
}

// ── Undo / Redo ─────────────────────────────────────────────────

#[test]
fn textarea_undo_reverts_change() {
    let mut pilot = Pilot::new(
        TextArea::new().text("initial").show_line_numbers(true),
        30, 8,
    );
    pilot.focus_first();

    // Type to change
    pilot.press(Key::End).unwrap();
    pilot.press(Key::Char('!')).unwrap();
    assert!(has_cell(pilot.frame(), "initial!"), "text after edit");

    // Undo (Ctrl+Z is not directly mappable, test via undo key if available)
    // For now, just verify the text is modifiable
}

// ── Empty textarea ──────────────────────────────────────────────

#[test]
fn textarea_empty_accepts_input() {
    let mut pilot = Pilot::new(
        TextArea::new().show_line_numbers(true),
        30, 8,
    );
    pilot.focus_first();

    pilot.press(Key::Char('f')).unwrap();
    pilot.press(Key::Char('i')).unwrap();
    pilot.press(Key::Char('r')).unwrap();
    pilot.press(Key::Char('s')).unwrap();
    pilot.press(Key::Char('t')).unwrap();
    assert!(has_cell(pilot.frame(), "first"), "empty textarea accepts chars");
}

// ── Multi-line editing ──────────────────────────────────────────

#[test]
fn textarea_multiline_edit() {
    let mut pilot = Pilot::new(
        TextArea::new()
            .text("line one\nline two\nline three")
            .show_line_numbers(true),
        40, 10,
    );
    pilot.focus_first();

    // Navigate to second line
    pilot.press(Key::Down).unwrap();
    // Edit on second line
    pilot.press(Key::End).unwrap();
    pilot.press(Key::Char('!')).unwrap();

    assert!(has_cell(pilot.frame(), "line two!"), "edit on second line");
    assert!(has_cell(pilot.frame(), "3"), "line number 3 visible");
}

// ── Focus shows cursor ─────────────────────────────────────────

#[test]
fn textarea_shows_content_when_focused() {
    let mut pilot = Pilot::new(
        TextArea::new().text("visible").show_line_numbers(true),
        30, 8,
    );
    pilot.focus_first();
    // Content should be visible when focused
    assert!(has_cell(pilot.frame(), "visible"), "content visible when focused");
}
