#![cfg(unix)]

use insta::assert_snapshot;
use zellij_integration_tests::{
    assert_same_rendered_grid, claim_first_terminal_and_wait_for_prompt, col, keys, normalized,
    split_down_and_wait_for_prompt, split_right_and_wait_for_prompt, start_zellij, Coord, Size,
    TestRunner, TestSession, PROMPT, TERMINAL_SIZE,
};

#[test]
fn cannot_split_terminals_vertically_when_active_terminal_is_too_small() {
    let mut zellij = TestRunner::new(Size { cols: 8, rows: 20 }).start();
    let terminal = zellij.expect_pty_spawn();
    terminal.output(PROMPT);
    zellij.wait_until("first terminal prompt rendered", |grid_snapshot| {
        grid_snapshot.cursor_is_at(col(2).row(1))
    });
    let (width_before_split, _) = terminal.wait_for_size("first terminal sized", |_, _| true);

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('r'));

    terminal.output(b"done");
    let tab_bar_glyph = '\u{e0b0}';
    let grid_snapshot = zellij.wait_until(
        "split attempt processed, chrome rendered",
        |grid_snapshot| {
            grid_snapshot.contains("done")
                && grid_snapshot.contains("Ctrl +")
                && grid_snapshot
                    .lines()
                    .first()
                    .is_some_and(|first_line| first_line.contains(tab_bar_glyph))
        },
    );

    assert_eq!(terminal.size().unwrap().0, width_before_split);
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn key_after_mode_switch_is_interpreted_in_new_mode_not_leaked_to_pane() {
    let mut zellij = start_zellij();
    let first_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('r'));

    let second_terminal = zellij.expect_pty_spawn();
    second_terminal.output(PROMPT);
    zellij.wait_until("split-right interpreted in pane mode", |grid_snapshot| {
        grid_snapshot.cursor_is_at(col(62).row(2))
    });

    assert!(!first_terminal.stdin_bytes().contains(&keys::key('r')[0]));
    zellij.quit();
}

#[test]
fn toggle_pane_fullscreen() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);
    split_right_and_wait_for_prompt(&zellij);

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('f'));

    let grid_snapshot = zellij.wait_until("focused pane is fullscreen", |grid_snapshot| {
        grid_snapshot.cursor_is_at(col(2).row(2))
            && grid_snapshot.contains("LOCK")
            && grid_snapshot.contains("(FULLSCREEN)")
    });
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn new_pane_while_fullscreen_breaks_out_of_fullscreen() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);
    split_right_and_wait_for_prompt(&zellij);

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('f'));
    zellij.wait_until("focused pane is fullscreen", |grid_snapshot| {
        grid_snapshot.contains("(FULLSCREEN)")
    });

    zellij.send_stdin(&keys::alt('n'));
    let new_terminal = zellij.expect_pty_spawn();
    new_terminal.output(PROMPT);

    let grid_snapshot = zellij.wait_until(
        "all three panes tiled after breaking out of fullscreen",
        |grid_snapshot| {
            !grid_snapshot.contains("(FULLSCREEN)")
                && grid_snapshot.status_bar_appears()
                && grid_snapshot.contains("Pane #1")
                && grid_snapshot.contains("Pane #2")
                && grid_snapshot.contains("Pane #3")
                && grid_snapshot.cursor.is_some()
        },
    );
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn closing_the_fullscreen_pane_restores_the_remaining_layout() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);
    split_right_and_wait_for_prompt(&zellij);
    let two_pane_state = zellij.wait_until("two panes settled", |grid_snapshot| {
        grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(62).row(2))
    });

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('d'));
    let third_terminal = zellij.expect_pty_spawn();
    third_terminal.output(PROMPT);
    zellij.wait_until("third pane split below the right pane", |grid_snapshot| {
        grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(62).row(13))
    });

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('f'));
    zellij.wait_until("third pane is fullscreen", |grid_snapshot| {
        grid_snapshot.contains("(FULLSCREEN)")
    });

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('x'));
    let after_close = zellij.wait_until(
        "fullscreen pane closed, two panes restored",
        |grid_snapshot| {
            !grid_snapshot.contains("(FULLSCREEN)")
                && grid_snapshot.status_bar_appears()
                && grid_snapshot.cursor_is_at(col(62).row(2))
        },
    );

    assert_same_rendered_grid(
        &after_close,
        &two_pane_state,
        "closing the fullscreen pane restores the pre-split layout",
    );
    zellij.quit();
}

#[test]
fn close_pane() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);
    split_right_and_wait_for_prompt(&zellij);

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('x'));

    let grid_snapshot =
        zellij.wait_until("right pane closed, focus back on first", |grid_snapshot| {
            grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(2).row(1))
        });
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn closing_last_pane_exits_zellij() {
    let zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('x'));

    zellij.wait_until(
        "zellij exited after closing the last pane",
        |grid_snapshot| grid_snapshot.contains("Bye from Zellij!"),
    );
}

#[test]
fn pane_closes_when_its_process_exits() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);
    let right_terminal = split_right_and_wait_for_prompt(&zellij);

    right_terminal.exit(Some(0));

    let grid_snapshot =
        zellij.wait_until("right pane closed, focus back on first", |grid_snapshot| {
            grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(2).row(1))
        });
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn toggle_floating_panes() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('w'));
    let floating_terminal = zellij.expect_pty_spawn();
    floating_terminal.output(PROMPT);

    let grid_snapshot = zellij.wait_until("floating pane appeared", |grid_snapshot| {
        grid_snapshot.cursor_is_at(col(33).row(8))
            && grid_snapshot.contains("STAGGERED")
            && grid_snapshot.contains("LOCK")
    });
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn undo_rename_pane() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);
    split_right_and_wait_for_prompt(&zellij);

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('c'));
    zellij.send_stdin(b"aa");

    zellij.wait_until("typed name applied in rename mode", |grid_snapshot| {
        grid_snapshot.contains("aa") && grid_snapshot.contains("RENAMING PANE")
    });

    zellij.send_stdin(&keys::ESC);
    zellij.wait_until("rename undone, back in pane mode", |grid_snapshot| {
        grid_snapshot.contains("Pane #2") && !grid_snapshot.contains("aa")
    });

    zellij.send_stdin(&keys::ESC);
    let grid_snapshot = zellij.wait_until("pane name reverted to default", |grid_snapshot| {
        grid_snapshot.contains("Pane #2")
            && !grid_snapshot.contains("aa")
            && grid_snapshot.contains("LOCK")
    });
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

fn spawn_second_pane_and_wait_for_grid(
    zellij: &TestSession,
    prompt: Coord,
) -> zellij_integration_tests::GridSnapshot {
    let terminal = zellij.expect_pty_spawn();
    terminal.output(PROMPT);
    zellij.wait_until(
        "second pane spawned and prompt rendered",
        move |grid_snapshot| {
            grid_snapshot.status_bar_appears()
                && grid_snapshot.contains("Pane #2")
                && grid_snapshot.cursor_is_at(prompt)
        },
    )
}

fn appears_in_order(
    grid_snapshot: &zellij_integration_tests::GridSnapshot,
    labels: &[&str],
) -> bool {
    let mut search_from = 0;
    for label in labels {
        match grid_snapshot.text[search_from..].find(label) {
            Some(offset) => search_from += offset + label.len(),
            None => return false,
        }
    }
    true
}

#[test]
fn move_focus_up_in_pane_mode() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);
    split_down_and_wait_for_prompt(&zellij);

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('k'));
    zellij.send_stdin(&keys::ENTER);

    let grid_snapshot = zellij.wait_until("focus moved to the upper pane", |grid_snapshot| {
        grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(2).row(2))
    });
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn move_focus_down_in_pane_mode() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);
    split_down_and_wait_for_prompt(&zellij);

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('k'));
    zellij.wait_until("focus moved to the upper pane", |grid_snapshot| {
        grid_snapshot.cursor_is_at(col(2).row(2))
    });

    zellij.send_stdin(&keys::key('j'));
    zellij.send_stdin(&keys::ENTER);

    let grid_snapshot = zellij.wait_until("focus moved back to the lower pane", |grid_snapshot| {
        grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(2).row(13))
    });
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn move_pane_swaps_with_neighbor() {
    let mut zellij = start_zellij();
    let left_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
    let right_terminal = split_right_and_wait_for_prompt(&zellij);
    left_terminal.output(b"AAA");
    right_terminal.output(b"BBB");
    zellij.wait_until("both panes labelled left to right", |grid_snapshot| {
        appears_in_order(grid_snapshot, &["AAA", "BBB"])
    });

    zellij.send_stdin(&keys::ctrl('h'));
    zellij.send_stdin(&keys::key('n'));
    zellij.send_stdin(&keys::ENTER);

    let grid_snapshot = zellij.wait_until("focused pane swapped to the left", |grid_snapshot| {
        appears_in_order(grid_snapshot, &["BBB", "AAA"]) && grid_snapshot.status_bar_appears()
    });
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn move_pane_backwards_swaps_with_neighbor() {
    let mut zellij = start_zellij();
    let left_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
    let right_terminal = split_right_and_wait_for_prompt(&zellij);
    left_terminal.output(b"AAA");
    right_terminal.output(b"BBB");
    zellij.wait_until("both panes labelled left to right", |grid_snapshot| {
        appears_in_order(grid_snapshot, &["AAA", "BBB"])
    });

    zellij.send_stdin(&keys::ctrl('h'));
    zellij.send_stdin(&keys::key('p'));
    zellij.send_stdin(&keys::ENTER);

    let grid_snapshot = zellij.wait_until("focused pane swapped backwards", |grid_snapshot| {
        appears_in_order(grid_snapshot, &["BBB", "AAA"]) && grid_snapshot.status_bar_appears()
    });
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn move_pane_left() {
    let mut zellij = start_zellij();
    let left_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
    let right_terminal = split_right_and_wait_for_prompt(&zellij);
    left_terminal.output(b"AAA");
    right_terminal.output(b"BBB");
    zellij.wait_until("both panes labelled left to right", |grid_snapshot| {
        appears_in_order(grid_snapshot, &["AAA", "BBB"])
    });

    zellij.send_stdin(&keys::ctrl('h'));
    zellij.send_stdin(&keys::key('h'));
    zellij.send_stdin(&keys::ENTER);

    let grid_snapshot = zellij.wait_until("focused pane moved to the left", |grid_snapshot| {
        appears_in_order(grid_snapshot, &["BBB", "AAA"]) && grid_snapshot.status_bar_appears()
    });
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn move_pane_down() {
    let mut zellij = start_zellij();
    let upper_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
    let lower_terminal = split_down_and_wait_for_prompt(&zellij);
    upper_terminal.output(b"AAA");
    lower_terminal.output(b"BBB");
    zellij.wait_until("both panes labelled top to bottom", |grid_snapshot| {
        appears_in_order(grid_snapshot, &["AAA", "BBB"])
    });

    zellij.send_stdin(&keys::ctrl('h'));
    zellij.send_stdin(&keys::key('k'));
    zellij.send_stdin(&keys::ENTER);

    let grid_snapshot = zellij.wait_until("focused pane moved upward", |grid_snapshot| {
        appears_in_order(grid_snapshot, &["BBB", "AAA"]) && grid_snapshot.status_bar_appears()
    });
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn toggle_pane_embed_or_floating() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);
    split_right_and_wait_for_prompt(&zellij);

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('e'));

    let grid_snapshot = zellij.wait_until("focused pane became floating", |grid_snapshot| {
        grid_snapshot.contains("STAGGERED")
            && grid_snapshot.contains("LOCK")
            && grid_snapshot.cursor_is_at(col(33).row(8))
    });
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn toggle_pane_pinned() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('w'));
    let floating_terminal = zellij.expect_pty_spawn();
    floating_terminal.output(PROMPT);
    zellij.wait_until("floating pane appeared", |grid_snapshot| {
        grid_snapshot.contains("STAGGERED")
    });

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('i'));

    let grid_snapshot = zellij.wait_until("floating pane pinned", |grid_snapshot| {
        grid_snapshot.contains("PIN [+]")
            && grid_snapshot.contains("LOCK")
            && grid_snapshot.cursor_is_at(col(33).row(8))
    });
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn move_focus_or_tab_moves_focus_between_panes() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);
    split_right_and_wait_for_prompt(&zellij);

    zellij.send_stdin(&keys::alt('h'));

    let grid_snapshot = zellij.wait_until("focus moved to the left pane", |grid_snapshot| {
        grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(2).row(2))
    });
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn move_focus_or_tab_switches_tab_when_single_pane() {
    let mut zellij = start_zellij();
    let first_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
    first_terminal.output(b"oneone");
    zellij.wait_until("first tab pane labelled", |grid_snapshot| {
        grid_snapshot.contains("oneone")
    });

    zellij.send_stdin(&keys::ctrl('t'));
    zellij.send_stdin(&keys::key('n'));
    let second_terminal = zellij.expect_pty_spawn();
    second_terminal.output(PROMPT);
    zellij.wait_until("second tab focused, first tab hidden", |grid_snapshot| {
        grid_snapshot.contains("Tab #2") && !grid_snapshot.contains("oneone")
    });

    zellij.send_stdin(&keys::alt('h'));

    let grid_snapshot = zellij
        .wait_until("focus switched back to the first tab", |grid_snapshot| {
            grid_snapshot.status_bar_appears() && grid_snapshot.contains("oneone")
        });
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn new_pane_in_pane_mode() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('n'));
    let grid_snapshot = spawn_second_pane_and_wait_for_grid(&zellij, col(62).row(2));
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn new_pane_in_normal_mode() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);

    zellij.send_stdin(&keys::alt('n'));
    let grid_snapshot = spawn_second_pane_and_wait_for_grid(&zellij, col(62).row(2));
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn split_pane_downward() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('d'));
    let grid_snapshot = spawn_second_pane_and_wait_for_grid(&zellij, col(2).row(13));
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn new_stacked_pane() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('s'));
    let grid_snapshot = spawn_second_pane_and_wait_for_grid(&zellij, col(2).row(4));
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn move_focus_left_in_pane_mode() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);
    split_right_and_wait_for_prompt(&zellij);

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('h'));
    zellij.send_stdin(&keys::ENTER);

    let grid_snapshot = zellij.wait_until("focus moved to the left pane", |grid_snapshot| {
        grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(2).row(2))
    });
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn move_focus_right_in_pane_mode() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);
    split_right_and_wait_for_prompt(&zellij);

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('h'));
    zellij.wait_until("focus moved to the left pane", |grid_snapshot| {
        grid_snapshot.cursor_is_at(col(2).row(2))
    });

    zellij.send_stdin(&keys::key('l'));
    zellij.send_stdin(&keys::ENTER);

    let grid_snapshot = zellij.wait_until("focus moved back to the right pane", |grid_snapshot| {
        grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(62).row(2))
    });
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn switch_focus_in_pane_mode() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);
    split_right_and_wait_for_prompt(&zellij);

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('p'));
    zellij.send_stdin(&keys::ENTER);

    let grid_snapshot = zellij.wait_until("focus switched to the other pane", |grid_snapshot| {
        grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(2).row(2))
    });
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

fn cycle_pane_frames(zellij: &TestSession) {
    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('z'));
}

#[test]
fn toggle_pane_frames() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);
    split_right_and_wait_for_prompt(&zellij);

    zellij.wait_until("tiled panes start in titles mode", |grid_snapshot| {
        grid_snapshot.contains("Pane #1")
            && grid_snapshot.contains("Pane #2")
            && !grid_snapshot.contains("┌")
    });

    cycle_pane_frames(&zellij);
    zellij.wait_until("titles cycle to frameless", |grid_snapshot| {
        grid_snapshot.status_bar_appears()
            && !grid_snapshot.contains("Pane #1")
            && !grid_snapshot.contains("Pane #2")
            && !grid_snapshot.contains("┌")
            && grid_snapshot.cursor_is_at(col(62).row(1))
    });

    cycle_pane_frames(&zellij);
    zellij.wait_until("frameless cycles to full frames", |grid_snapshot| {
        grid_snapshot.contains("┌")
            && grid_snapshot.contains("Pane #1")
            && grid_snapshot.contains("Pane #2")
    });

    cycle_pane_frames(&zellij);
    let grid_snapshot = zellij.wait_until("full frames cycle back to titles", |grid_snapshot| {
        grid_snapshot.status_bar_appears()
            && grid_snapshot.contains("Pane #1")
            && grid_snapshot.contains("Pane #2")
            && !grid_snapshot.contains("┌")
    });
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn toggle_frames_with_single_pane() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);

    zellij.wait_until(
        "lone pane in titles mode omits its title",
        |grid_snapshot| {
            grid_snapshot.status_bar_appears()
                && !grid_snapshot.contains("Pane #1")
                && !grid_snapshot.contains("┌")
        },
    );

    cycle_pane_frames(&zellij);
    zellij.wait_until("lone frameless pane stays untitled", |grid_snapshot| {
        grid_snapshot.status_bar_appears()
            && !grid_snapshot.contains("Pane #1")
            && !grid_snapshot.contains("┌")
    });

    cycle_pane_frames(&zellij);
    let grid_snapshot =
        zellij.wait_until("lone pane gains a full frame and title", |grid_snapshot| {
            grid_snapshot.status_bar_appears()
                && grid_snapshot.contains("┌")
                && grid_snapshot.contains("Pane #1")
        });
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn toggle_frames_with_floating_pane() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('w'));
    let floating_terminal = zellij.expect_pty_spawn();
    floating_terminal.output(PROMPT);
    zellij.wait_until("floating pane appeared with a frame", |grid_snapshot| {
        grid_snapshot.contains("STAGGERED") && grid_snapshot.contains("┌")
    });

    cycle_pane_frames(&zellij);
    zellij.wait_until(
        "floating pane keeps its frame in frameless mode",
        |grid_snapshot| grid_snapshot.status_bar_appears() && grid_snapshot.contains("┌"),
    );

    cycle_pane_frames(&zellij);
    let grid_snapshot = zellij.wait_until(
        "floating pane keeps its frame in full mode",
        |grid_snapshot| {
            grid_snapshot.status_bar_appears()
                && grid_snapshot.contains("┌")
                && grid_snapshot.contains("Pane #1")
        },
    );
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn toggle_frames_with_stacked_panes() {
    let mut zellij = start_zellij();
    claim_first_terminal_and_wait_for_prompt(&zellij);

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('s'));
    spawn_second_pane_and_wait_for_grid(&zellij, col(2).row(4));

    zellij.wait_until("stacked panes show their titles", |grid_snapshot| {
        grid_snapshot.contains("Pane #1")
            && grid_snapshot.contains("Pane #2")
            && !grid_snapshot.contains("┌")
    });

    cycle_pane_frames(&zellij);
    zellij.wait_until("stacked panes cycle to frameless", |grid_snapshot| {
        grid_snapshot.status_bar_appears() && !grid_snapshot.contains("┌")
    });

    cycle_pane_frames(&zellij);
    let grid_snapshot = zellij.wait_until("stacked panes cycle to full frames", |grid_snapshot| {
        grid_snapshot.status_bar_appears()
            && grid_snapshot.contains("┌")
            && grid_snapshot.contains("Pane #1")
            && grid_snapshot.contains("Pane #2")
    });
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}

#[test]
fn start_without_pane_frames() {
    let mut zellij = TestRunner::new(TERMINAL_SIZE)
        .with_config("pane_frames false")
        .start();
    let first_terminal = zellij.expect_pty_spawn();
    first_terminal.output(PROMPT);
    zellij.wait_until(
        "first frameless terminal prompt rendered",
        |grid_snapshot| {
            grid_snapshot.tab_bar_appears()
                && grid_snapshot.status_bar_appears()
                && grid_snapshot.cursor_is_at(col(2).row(1))
        },
    );

    zellij.send_stdin(&keys::ctrl('p'));
    zellij.send_stdin(&keys::key('r'));
    let right_terminal = zellij.expect_pty_spawn();
    right_terminal.output(PROMPT);

    let grid_snapshot = zellij.wait_until(
        "right frameless terminal prompt rendered",
        |grid_snapshot| {
            grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(62).row(1))
        },
    );
    assert_snapshot!(normalized(&grid_snapshot));
    zellij.quit();
}
