//! Matrix coverage for app-lifecycle commands (ADR-0003):
//! quit, help, rebuild, save / save as, new, load, export,
//! import, mode, messages.
//!
//! App commands don't touch the schema, so the empty schema is
//! sufficient.

use crate::typing_surface::*;
use rdbms_playground::input_render::InputState;

#[test]
fn quit_parses() {
    let a = assess_at_end("quit", &schema_empty());
    assert!(matches!(a.state, InputState::Valid));
    assert_eq!(a.parse_result.as_deref(), Ok("App(Quit)"));
    crate::snap!("quit", a);
}

#[test]
fn help_parses() {
    let a = assess_at_end("help", &schema_empty());
    assert!(matches!(a.state, InputState::Valid));
    assert_eq!(a.parse_result.as_deref(), Ok("App(Help)"));
    crate::snap!("help", a);
}

#[test]
fn rebuild_parses() {
    let a = assess_at_end("rebuild", &schema_empty());
    assert!(matches!(a.state, InputState::Valid));
    crate::snap!("rebuild", a);
}

#[test]
fn save_parses() {
    let a = assess_at_end("save", &schema_empty());
    assert!(matches!(a.state, InputState::Valid));
    assert_eq!(a.parse_result.as_deref(), Ok("App(Save)"));
    crate::snap!("save", a);
}

#[test]
fn save_space_offers_as_keyword() {
    // Handoff-12 §3 smoke item: `save ` then Tab offers `as`.
    let a = assess_at_end("save ", &schema_empty());
    assert_candidate_present(&a, &["as"]);
    crate::snap!("save_space", a);
}

#[test]
fn save_as_parses() {
    let a = assess_at_end("save as", &schema_empty());
    assert!(matches!(a.state, InputState::Valid));
    assert_eq!(a.parse_result.as_deref(), Ok("App(SaveAs)"));
    crate::snap!("save_as", a);
}

#[test]
fn new_parses() {
    let a = assess_at_end("new", &schema_empty());
    assert!(matches!(a.state, InputState::Valid));
    crate::snap!("new", a);
}

#[test]
fn load_parses() {
    let a = assess_at_end("load", &schema_empty());
    assert!(matches!(a.state, InputState::Valid));
    crate::snap!("load", a);
}

#[test]
fn export_with_no_path_parses() {
    // Export path is optional — `export` alone opens the modal.
    let a = assess_at_end("export", &schema_empty());
    assert!(matches!(a.state, InputState::Valid));
    crate::snap!("export_no_path", a);
}

#[test]
fn export_with_path_parses() {
    let a = assess_at_end("export myproject.zip", &schema_empty());
    assert!(matches!(a.state, InputState::Valid));
    crate::snap!("export_with_path", a);
}

#[test]
fn import_with_path_parses() {
    let a = assess_at_end("import bundle.zip", &schema_empty());
    assert!(matches!(a.state, InputState::Valid));
    crate::snap!("import_with_path", a);
}

#[test]
fn mode_with_value_parses() {
    let a = assess_at_end("mode advanced", &schema_empty());
    assert!(matches!(a.state, InputState::Valid));
    crate::snap!("mode_advanced", a);
}

#[test]
fn mode_space_offers_simple_and_advanced() {
    let a = assess_at_end("mode ", &schema_empty());
    assert!(matches!(a.state, InputState::IncompleteAtEof));
    assert_candidate_present(&a, &["simple", "advanced"]);
    crate::snap!("mode_space", a);
}

#[test]
fn messages_with_no_value_parses() {
    // Messages value is optional — `messages` alone shows the
    // current verbosity.
    let a = assess_at_end("messages", &schema_empty());
    assert!(matches!(a.state, InputState::Valid));
    crate::snap!("messages_no_value", a);
}

#[test]
fn messages_space_offers_short_and_verbose() {
    let a = assess_at_end("messages ", &schema_empty());
    assert_candidate_present(&a, &["short", "verbose"]);
    crate::snap!("messages_space", a);
}

#[test]
fn messages_with_value_parses() {
    let a = assess_at_end("messages verbose", &schema_empty());
    assert!(matches!(a.state, InputState::Valid));
    crate::snap!("messages_verbose", a);
}

#[test]
fn copy_with_no_value_parses() {
    // Copy scope is optional — bare `copy` copies the whole panel.
    let a = assess_at_end("copy", &schema_empty());
    assert!(matches!(a.state, InputState::Valid));
    assert_eq!(a.parse_result.as_deref(), Ok("App(Copy)"));
    crate::snap!("copy_no_value", a);
}

#[test]
fn copy_all_parses() {
    let a = assess_at_end("copy all", &schema_empty());
    assert!(matches!(a.state, InputState::Valid));
    assert_eq!(a.parse_result.as_deref(), Ok("App(Copy)"));
    crate::snap!("copy_all", a);
}

#[test]
fn copy_last_parses() {
    let a = assess_at_end("copy last", &schema_empty());
    assert!(matches!(a.state, InputState::Valid));
    assert_eq!(a.parse_result.as_deref(), Ok("App(Copy)"));
    crate::snap!("copy_last", a);
}

#[test]
fn copy_space_offers_all_and_last() {
    let a = assess_at_end("copy ", &schema_empty());
    assert_candidate_present(&a, &["all", "last"]);
    crate::snap!("copy_space", a);
}

#[test]
fn partial_entry_word_classifies_as_definite_error_but_completes() {
    // `qu` — mid-typing the `quit` entry word.
    //
    // KNOWN UX WRINKLE (handoff-13 finding): a partial entry
    // word is currently classified `DefiniteErrorAt(0)` — the
    // same as a genuinely unknown command (`frobulate`). The
    // walker only engages once a *complete* registered entry
    // word is present; until then `try_walker_route` returns
    // None and the router emits the synthetic unknown-command
    // error. So the input pane shows `qu` in error-red even
    // though it is a valid prefix of `quit`.
    //
    // Completion still works (Tab at `qu` → `quit`), so the
    // user can recover, but the red overlay while typing the
    // first word is jarring. This test documents the current
    // behaviour; flagged to the user for a decision rather
    // than silently asserting it as correct.
    let a = assess_at_end("qu", &schema_empty());
    assert!(
        matches!(a.state, InputState::DefiniteErrorAt(_)),
        "documents current behaviour, got {:?}",
        a.state,
    );
    // Completion recovers — `quit` is offered.
    assert_candidate_present(&a, &["quit"]);
    crate::snap!("partial_quit", a);
}
