use super::{
    InputAggregator, input_device_override_from_env, matches_selected_input_device,
    pending_release_timeout_from_env, quick_toggle_debounce_from_env,
    remote_failsafe_timeout_from_env, update_shadow_pressed_keys,
};
use evdev::KeyCode;
use serial_test::serial;
use std::{
    collections::HashSet,
    path::Path,
    sync::atomic::Ordering,
    time::{Duration, Instant},
};

use super::parse_quick_toggle_key;

#[test]
fn parse_quick_toggle_key_supports_letters_digits_and_function_keys() {
    assert_eq!(parse_quick_toggle_key("a"), Some(KeyCode::KEY_A));
    assert_eq!(parse_quick_toggle_key("7"), Some(KeyCode::KEY_7));
    assert_eq!(parse_quick_toggle_key("f12"), Some(KeyCode::KEY_F12));
    assert_eq!(parse_quick_toggle_key("F3"), Some(KeyCode::KEY_F3));
}

#[test]
fn parse_quick_toggle_key_supports_navigation_and_special_aliases() {
    assert_eq!(parse_quick_toggle_key("page_up"), Some(KeyCode::KEY_PAGEUP));
    assert_eq!(parse_quick_toggle_key("delete"), Some(KeyCode::KEY_DELETE));
    assert_eq!(parse_quick_toggle_key("spacebar"), Some(KeyCode::KEY_SPACE));
    assert_eq!(
        parse_quick_toggle_key("print-screen"),
        Some(KeyCode::KEY_SYSRQ)
    );
}

#[test]
fn parse_quick_toggle_key_can_disable_or_fall_back() {
    assert_eq!(parse_quick_toggle_key("off"), None);
    assert_eq!(
        parse_quick_toggle_key("totally-unknown"),
        Some(KeyCode::KEY_PAUSE)
    );
}

#[test]
#[serial]
fn runtime_timeouts_apply_safe_defaults_and_floors() {
    temp_env::with_vars(
        [
            ("LESAVKA_INPUT_TOGGLE_DEBOUNCE_MS", None::<&str>),
            ("LESAVKA_INPUT_RELEASE_TIMEOUT_MS", None::<&str>),
            ("LESAVKA_INPUT_REMOTE_FAILSAFE_MS", None::<&str>),
            ("LESAVKA_INPUT_REMOTE_FAILSAFE_SECS", None::<&str>),
        ],
        || {
            assert_eq!(quick_toggle_debounce_from_env(), Duration::from_millis(350));
            assert_eq!(
                pending_release_timeout_from_env(),
                Duration::from_millis(750)
            );
            assert_eq!(remote_failsafe_timeout_from_env(), Duration::ZERO);
        },
    );

    temp_env::with_vars(
        [
            ("LESAVKA_INPUT_TOGGLE_DEBOUNCE_MS", Some("1")),
            ("LESAVKA_INPUT_RELEASE_TIMEOUT_MS", Some("1")),
            ("LESAVKA_INPUT_REMOTE_FAILSAFE_MS", Some("250")),
            ("LESAVKA_INPUT_REMOTE_FAILSAFE_SECS", None::<&str>),
        ],
        || {
            assert_eq!(quick_toggle_debounce_from_env(), Duration::from_millis(50));
            assert_eq!(
                pending_release_timeout_from_env(),
                Duration::from_millis(100)
            );
            assert_eq!(
                remote_failsafe_timeout_from_env(),
                Duration::from_millis(250)
            );
        },
    );

    temp_env::with_vars(
        [
            ("LESAVKA_INPUT_REMOTE_FAILSAFE_MS", Some("250")),
            ("LESAVKA_INPUT_REMOTE_FAILSAFE_SECS", Some("2")),
        ],
        || {
            assert_eq!(remote_failsafe_timeout_from_env(), Duration::from_secs(2));
        },
    );
}

#[test]
#[serial]
fn input_device_overrides_ignore_empty_and_all_values() {
    temp_env::with_var("LESAVKA_KEYBOARD_DEVICE", Some("/dev/input/event4"), || {
        assert_eq!(
            input_device_override_from_env("LESAVKA_KEYBOARD_DEVICE").as_deref(),
            Some("/dev/input/event4")
        );
    });

    for raw in ["", "   ", "all", "ALL"] {
        temp_env::with_var("LESAVKA_KEYBOARD_DEVICE", Some(raw), || {
            assert_eq!(
                input_device_override_from_env("LESAVKA_KEYBOARD_DEVICE"),
                None
            );
        });
    }
}

#[test]
fn selected_input_device_matches_exact_paths_or_all_devices() {
    let event_path = Path::new("/dev/input/event7");

    assert!(matches_selected_input_device(event_path, None));
    assert!(matches_selected_input_device(
        event_path,
        Some("/dev/input/event7")
    ));
    assert!(!matches_selected_input_device(
        event_path,
        Some("/dev/input/event8")
    ));
}

#[test]
fn shadow_pressed_keys_tracks_press_hold_and_release() {
    let mut pressed = HashSet::new();

    update_shadow_pressed_keys(&mut pressed, KeyCode::KEY_A, 1);
    update_shadow_pressed_keys(&mut pressed, KeyCode::KEY_B, 2);
    update_shadow_pressed_keys(&mut pressed, KeyCode::KEY_C, -1);
    assert!(pressed.contains(&KeyCode::KEY_A));
    assert!(pressed.contains(&KeyCode::KEY_B));
    assert!(!pressed.contains(&KeyCode::KEY_C));

    update_shadow_pressed_keys(&mut pressed, KeyCode::KEY_A, 0);
    assert!(!pressed.contains(&KeyCode::KEY_A));
}

#[test]
#[serial]
fn input_aggregator_respects_capture_boot_and_failsafe_state() {
    let (kbd_tx, _) = tokio::sync::broadcast::channel(4);
    let (mou_tx, _) = tokio::sync::broadcast::channel(4);

    temp_env::with_vars(
        [
            ("LESAVKA_INPUT_REMOTE_FAILSAFE_MS", Some("500")),
            ("LESAVKA_INPUT_REMOTE_FAILSAFE_SECS", None::<&str>),
            ("LESAVKA_INPUT_TOGGLE_DEBOUNCE_MS", Some("50")),
            ("LESAVKA_INPUT_RELEASE_TIMEOUT_MS", Some("100")),
        ],
        || {
            let mut aggregator =
                InputAggregator::new_with_capture_mode(false, kbd_tx, mou_tx, None, true);

            assert!(!aggregator.released);
            assert!(aggregator.remote_capture_active());
            assert!(
                aggregator
                    .remote_capture_enabled_handle()
                    .load(Ordering::Relaxed)
            );
            assert_eq!(aggregator.quick_toggle_debounce, Duration::from_millis(50));
            assert_eq!(
                aggregator.pending_release_timeout,
                Duration::from_millis(100)
            );
            assert_eq!(
                aggregator.remote_failsafe_timeout,
                Duration::from_millis(500)
            );

            aggregator.remote_failsafe_started_at =
                Some(Instant::now() - Duration::from_millis(750));
            assert!(aggregator.remote_failsafe_expired());

            aggregator.begin_local_release();
            assert!(!aggregator.remote_capture_active());
            assert!(aggregator.pending_release);
            aggregator.pending_release_started_at =
                Some(Instant::now() - Duration::from_millis(150));
            assert!(aggregator.pending_release_timed_out());

            aggregator.finish_local_release(false);
            assert!(aggregator.released);
            assert!(!aggregator.pending_release);

            aggregator.enable_remote_capture();
            assert!(!aggregator.released);
            assert!(aggregator.remote_capture_active());
        },
    );
}
