// Simulated disconnect safety contracts for input.
//
// Scope: source-level contracts for release-on-disconnect behavior.
// Targets: `client/src/input/keyboard.rs`, `client/src/input/mouse.rs`, and
// `client/src/input/inputs/routing_state.rs`.
// Why: the safest disconnected input is a released input; regressions here can
// leave remote modifiers/buttons stuck or leak typing into the wrong target.

const KEYBOARD_REPORTING_SRC: &str =
    include_str!("../../../client/src/input/keyboard/reporting.rs");
const MOUSE_SRC: &str = include_str!(env!("LESAVKA_CLIENT_MOUSE_SRC"));
const INPUT_ROUTING_SRC: &str = include_str!("../../../client/src/input/inputs/routing_state.rs");

fn block_after<'a>(source: &'a str, marker: &str) -> &'a str {
    let start = source.find(marker).expect("marker exists");
    &source[start..]
}

#[test]
fn keyboard_drop_always_sends_empty_report_to_release_pressed_keys() {
    let drop_impl = block_after(KEYBOARD_REPORTING_SRC, "impl Drop for KeyboardAggregator");

    assert!(drop_impl.contains("data: [0; 8].into()"));
}

#[test]
fn mouse_drop_uses_mouse_sized_empty_report_to_release_buttons() {
    let drop_impl = block_after(MOUSE_SRC, "impl Drop for MouseAggregator");
    let drop_body = drop_impl
        .split_once("#[cfg(test)]")
        .map(|(body, _)| body)
        .unwrap_or(drop_impl);

    assert!(drop_body.contains("data: [0; 4].into()"));
    assert!(
        !drop_body.contains("data: [0; 8].into()"),
        "mouse disconnect release must not send keyboard-sized reports"
    );
}

#[test]
fn local_release_sends_zero_reports_before_disabling_forwarding() {
    let release = block_after(INPUT_ROUTING_SRC, "fn begin_local_release");
    let keyboard_zero = release
        .find("k.send_empty_report()")
        .expect("keyboard release");
    let keyboard_disable = release.find("k.set_send(false)").expect("keyboard disable");
    let mouse_reset = release.find("m.reset_state()").expect("mouse release");
    let mouse_disable = release.find("m.set_send(false)").expect("mouse disable");

    assert!(
        keyboard_zero < keyboard_disable,
        "keyboard release report must be sent before forwarding is disabled"
    );
    assert!(
        mouse_reset < mouse_disable,
        "mouse release report must be sent before forwarding is disabled"
    );
}

#[test]
fn finishing_local_release_resets_state_after_ungrabbing_devices() {
    let finish = block_after(INPUT_ROUTING_SRC, "fn finish_local_release");

    assert!(finish.contains("k.set_grab(false);"));
    assert!(finish.contains("k.reset_state();"));
    assert!(finish.contains("m.set_grab(false);"));
    assert!(finish.contains("m.reset_state();"));
}
