// Extra pure HID encoder edge coverage.
//
// Scope: verify balanced modifier/key release sequences without touching
// evdev, uinput, or gadget device nodes.
// Targets: `common/src/hid.rs`.
// Why: a leaked shift/modifier or missing release is exactly how a safe
// synthetic input path turns into foreground typing like `aAaasa`.

use lesavka_common::hid::{KeyboardHidReport, append_char_reports, char_to_usage};

fn reports_for(text: &str) -> Vec<KeyboardHidReport> {
    let mut reports = Vec::new();
    for ch in text.chars() {
        assert!(append_char_reports(&mut reports, ch), "unsupported {ch:?}");
    }
    reports
}

#[test]
fn shifted_character_releases_modifier_before_following_unshifted_character() {
    let reports = reports_for("Aa");

    assert_eq!(reports.len(), 6);
    assert_eq!(reports[0], [0x02, 0, 0, 0, 0, 0, 0, 0]);
    assert_eq!(reports[1], [0x02, 0, 0x04, 0, 0, 0, 0, 0]);
    assert_eq!(reports[2], [0x02, 0, 0, 0, 0, 0, 0, 0]);
    assert_eq!(reports[3], [0; 8]);
    assert_eq!(reports[4], [0, 0, 0x04, 0, 0, 0, 0, 0]);
    assert_eq!(reports[5], [0; 8]);
}

#[test]
fn whitespace_characters_emit_press_release_pairs_without_sticky_keys() {
    let reports = reports_for("\n\r\t ");
    let expected_usages = [0x28, 0x28, 0x2B, 0x2C];

    assert_eq!(reports.len(), expected_usages.len() * 2);
    for (chunk, usage) in reports.chunks_exact(2).zip(expected_usages) {
        assert_eq!(chunk[0], [0, 0, usage, 0, 0, 0, 0, 0]);
        assert_eq!(chunk[1], [0; 8]);
    }
}

#[test]
fn shifted_symbols_are_balanced_modifier_key_release_sequences() {
    for ch in [
        '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '{', '}', '|', ':', '"', '~',
        '<', '>', '?',
    ] {
        let mut reports = Vec::new();
        let (usage, modifiers) = char_to_usage(ch).expect("supported shifted symbol");

        assert_eq!(modifiers, 0x02, "{ch:?} should require shift");
        assert!(append_char_reports(&mut reports, ch));

        assert_eq!(
            reports,
            vec![
                [0x02, 0, 0, 0, 0, 0, 0, 0],
                [0x02, 0, usage, 0, 0, 0, 0, 0],
                [0x02, 0, 0, 0, 0, 0, 0, 0],
                [0; 8],
            ]
        );
    }
}
