/// Reads debounce window from env, with a safety floor to avoid rapid flapping.
fn quick_toggle_debounce_from_env() -> Duration {
    let millis = std::env::var("LESAVKA_INPUT_TOGGLE_DEBOUNCE_MS")
        .ok()
        .and_then(|raw| raw.parse::<u64>().ok())
        .unwrap_or(350);
    Duration::from_millis(millis.max(50))
}

fn pending_release_timeout_from_env() -> Duration {
    let millis = std::env::var("LESAVKA_INPUT_RELEASE_TIMEOUT_MS")
        .ok()
        .and_then(|raw| raw.parse::<u64>().ok())
        .unwrap_or(750);
    Duration::from_millis(millis.max(100))
}

fn remote_failsafe_timeout_from_env() -> Duration {
    if let Some(secs) = std::env::var("LESAVKA_INPUT_REMOTE_FAILSAFE_SECS")
        .ok()
        .and_then(|raw| raw.parse::<u64>().ok())
    {
        return Duration::from_secs(secs);
    }
    let millis = std::env::var("LESAVKA_INPUT_REMOTE_FAILSAFE_MS")
        .ok()
        .and_then(|raw| raw.parse::<u64>().ok())
        .unwrap_or(0);
    Duration::from_millis(millis)
}

#[cfg(not(coverage))]
fn input_rescan_interval_from_env() -> Duration {
    let millis = std::env::var("LESAVKA_INPUT_RESCAN_MS")
        .ok()
        .and_then(|raw| raw.parse::<u64>().ok())
        .unwrap_or(1_000);
    if millis == 0 {
        Duration::ZERO
    } else {
        Duration::from_millis(millis.max(250))
    }
}

#[cfg(not(coverage))]
fn input_device_identity(path: &Path) -> Option<u64> {
    std::fs::metadata(path)
        .ok()
        .map(|metadata| metadata.ino() ^ metadata.rdev())
}

#[cfg(not(coverage))]
fn focus_launcher_on_local_if_enabled() {
    if std::env::var("LESAVKA_FOCUS_LAUNCHER_ON_LOCAL")
        .map(|raw| raw.trim() == "0")
        .unwrap_or(false)
    {
        return;
    }
    let focus_signal_path = std::env::var("LESAVKA_LAUNCHER_FOCUS_SIGNAL")
        .unwrap_or_else(|_| "/tmp/lesavka-launcher-focus.signal".to_string());
    let _ = std::fs::write(
        focus_signal_path,
        format!(
            "{}\n",
            std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .map(|duration| duration.as_millis())
                .unwrap_or_default()
        ),
    );
    let title =
        std::env::var("LESAVKA_LAUNCHER_WINDOW_TITLE").unwrap_or_else(|_| "Lesavka".to_string());
    let _ = std::process::Command::new("wmctrl")
        .args(["-a", &title])
        .status();
}

#[cfg(not(coverage))]
fn launcher_routing_path_from_env(key: &str) -> Option<PathBuf> {
    std::env::var(key)
        .ok()
        .map(PathBuf::from)
        .filter(|path| !path.as_os_str().is_empty())
}

fn input_device_override_from_env(key: &str) -> Option<String> {
    std::env::var(key)
        .ok()
        .map(|raw| raw.trim().to_string())
        .filter(|raw| !raw.is_empty() && !raw.eq_ignore_ascii_case("all"))
}

fn matches_selected_input_device(path: &std::path::Path, selected: Option<&str>) -> bool {
    selected.is_none_or(|selected| path.to_string_lossy() == selected)
}

#[cfg(not(coverage))]
fn read_launcher_control_snapshot(path: &Path) -> Option<String> {
    let raw = std::fs::read_to_string(path).ok()?;
    let trimmed = raw.trim();
    (!trimmed.is_empty()).then(|| trimmed.to_string())
}

#[cfg(not(coverage))]
fn parse_launcher_routing_request(raw: &str) -> Option<bool> {
    match raw
        .split_ascii_whitespace()
        .next()?
        .to_ascii_lowercase()
        .as_str()
    {
        "remote" => Some(true),
        "local" => Some(false),
        _ => None,
    }
}

#[cfg(not(coverage))]
fn parse_wake_interval(raw: &str) -> Option<Duration> {
    match raw
        .split_ascii_whitespace()
        .next()?
        .to_ascii_lowercase()
        .as_str()
    {
        "off" | "0" => None,
        "5" | "5m" => Some(Duration::from_secs(5 * 60)),
        "10" | "10m" => Some(Duration::from_secs(10 * 60)),
        "20" | "20m" => Some(Duration::from_secs(20 * 60)),
        "30" | "30m" => Some(Duration::from_secs(30 * 60)),
        "60" | "60m" => Some(Duration::from_secs(60 * 60)),
        _ => None,
    }
}

#[cfg(not(coverage))]
fn path_marker(path: &Path) -> u128 {
    std::fs::metadata(path)
        .ok()
        .and_then(|meta| meta.modified().ok())
        .and_then(|stamp| stamp.duration_since(std::time::UNIX_EPOCH).ok())
        .map(|duration| duration.as_millis())
        .unwrap_or_default()
}
