nex-launch 2.1.0

A keyboard-first launcher for Windows
use crate::config::{self};
use crate::runtime::{
    load_query_profile_status_report, load_status_diagnostics_snapshot, log_info, log_warn,
    run_with_options, RuntimeError, RuntimeOptions,
};
#[cfg(target_os = "windows")]
use crate::runtime_process::{
    inspect_runtime_process_state, stop_runtime_instance, StopRuntimeOutcome,
};

pub(crate) fn command_ensure_config() -> Result<(), RuntimeError> {
    let cfg = config::load(None)?;
    if !cfg.config_path.exists() {
        config::write_user_template(&cfg, &cfg.config_path)?;
        log_info(&format!(
            "[nex] wrote user config template to {}",
            cfg.config_path.display()
        ));
    }
    log_info(&format!(
        "[nex] config ready at {}",
        cfg.config_path.display()
    ));
    Ok(())
}

pub(crate) fn command_sync_startup() -> Result<(), RuntimeError> {
    #[cfg(target_os = "windows")]
    {
        let cfg = config::load(None)?;
        let exe = std::env::current_exe()?;
        crate::startup::set_enabled(cfg.launch_at_startup, &exe)?;
        log_info(&format!(
            "[nex] startup registration synced: enabled={}",
            cfg.launch_at_startup
        ));
        return Ok(());
    }

    #[cfg(not(target_os = "windows"))]
    {
        log_info("[nex] startup sync is unsupported on this platform");
        Ok(())
    }
}

pub(crate) fn command_set_launch_at_startup(enabled: bool) -> Result<(), RuntimeError> {
    let mut cfg = config::load(None)?;
    cfg.launch_at_startup = enabled;
    config::save(&cfg)?;

    #[cfg(target_os = "windows")]
    {
        let exe = std::env::current_exe()?;
        crate::startup::set_enabled(enabled, &exe)?;
    }

    log_info(&format!(
        "[nex] launch_at_startup updated: enabled={} (can be changed in config)",
        enabled
    ));
    Ok(())
}

pub(crate) fn command_status() -> Result<(), RuntimeError> {
    #[cfg(target_os = "windows")]
    {
        let state = inspect_runtime_process_state();
        let running = state.has_overlay_window;
        log_info(&format!(
            "[nex] status: {}",
            if running {
                "running"
            } else if !state.other_runtime_pids.is_empty() {
                "degraded (process without overlay window)"
            } else {
                "stopped"
            }
        ));
        if !state.other_runtime_pids.is_empty() {
            log_warn(&format!(
                "[nex] status detected runtime_pids_without_window={:?} recommendation=run --restart",
                state.other_runtime_pids
            ));
        }
        if let Some(snapshot) = load_status_diagnostics_snapshot() {
            if let Some(line) = snapshot.hotkey_registration_issue_line {
                log_warn(&format!("[nex] status last_hotkey_issue {line}"));
            }
            if let Some(line) = snapshot.overlay_ready_line {
                log_info(&format!("[nex] status last_overlay_ready {line}"));
            }
            if let Some(line) = snapshot.hotkey_ready_line {
                log_info(&format!("[nex] status last_hotkey_ready {line}"));
            }
            if let Some(line) = snapshot.indexing_started_line {
                log_info(&format!("[nex] status last_indexing_started {line}"));
            }
            if let Some(line) = snapshot.indexing_completed_line {
                log_info(&format!("[nex] status last_indexing_completed {line}"));
            }
            if let Some(line) = snapshot.cache_applied_line {
                log_info(&format!("[nex] status last_cache_applied {line}"));
            }
            if let Some(line) = snapshot.startup_index_line {
                log_info(&format!("[nex] status last_indexing {line}"));
            }
            if let Some(line) = snapshot.last_provider_line {
                log_info(&format!("[nex] status last_provider {line}"));
            }
            if let Some(line) = snapshot.last_provider_freshness_line {
                log_info(&format!("[nex] status last_provider_freshness {line}"));
            }
            if let Some(line) = snapshot.last_stale_prune_line {
                log_info(&format!("[nex] status last_stale_prune {line}"));
            }
            if let Some(line) = snapshot.last_cache_compaction_line {
                log_info(&format!("[nex] status last_cache_compaction {line}"));
            }
            if let Some(line) = snapshot.last_icon_cache_line {
                log_info(&format!("[nex] status last_icon_cache {line}"));
            }
            if let Some(line) = snapshot.last_overlay_tuning_line {
                log_info(&format!("[nex] status last_overlay_tuning {line}"));
            }
            if let Some(line) = snapshot.last_memory_snapshot_line {
                log_info(&format!("[nex] status last_memory_snapshot {line}"));
            }
            if let Some(line) = snapshot.last_config_reload_line {
                log_info(&format!("[nex] status last_config_reload {line}"));
            }
        }
        if let Some(report) = load_query_profile_status_report() {
            if let Some(recent) = report.recent {
                log_info(&format!(
                    "[nex] status query_latency_recent samples={} p50_ms={} p95_ms={} p99_ms={} max_ms={} avg_ms={} indexed_p95_ms={} short_q_samples={} short_q_p95_ms={} short_q_app_bias_rate={}%",
                    recent.samples,
                    recent.p50_total_ms,
                    recent.p95_total_ms,
                    recent.p99_total_ms,
                    recent.max_total_ms,
                    recent.avg_total_ms,
                    recent.p95_indexed_ms,
                    recent.short_query_samples,
                    recent.short_query_p95_total_ms,
                    recent.short_query_app_bias_rate_pct
                ));
            }
            if let Some(historical) = report.historical {
                log_info(&format!(
                    "[nex] status query_latency_historical samples={} p50_ms={} p95_ms={} p99_ms={} max_ms={} avg_ms={} indexed_p95_ms={} short_q_samples={} short_q_p95_ms={} short_q_app_bias_rate={}%",
                    historical.samples,
                    historical.p50_total_ms,
                    historical.p95_total_ms,
                    historical.p99_total_ms,
                    historical.max_total_ms,
                    historical.avg_total_ms,
                    historical.p95_indexed_ms,
                    historical.short_query_samples,
                    historical.short_query_p95_total_ms,
                    historical.short_query_app_bias_rate_pct
                ));
            }
            log_info(&format!(
                "[nex] status query_guard recent_skipped_symbol_queries={} historical_skipped_symbol_queries={}",
                report.recent_skipped_symbol_queries, report.historical_skipped_symbol_queries
            ));
        }
        return Ok(());
    }

    #[cfg(not(target_os = "windows"))]
    {
        log_info("[nex] status: unsupported on this platform");
        Ok(())
    }
}

pub(crate) fn command_quit() -> Result<(), RuntimeError> {
    #[cfg(target_os = "windows")]
    {
        match stop_runtime_instance(std::time::Duration::from_secs(3))? {
            StopRuntimeOutcome::AlreadyStopped => {
                log_info("[nex] quit skipped (not running)");
                Ok(())
            }
            StopRuntimeOutcome::Graceful => {
                log_info("[nex] quit completed (graceful)");
                Ok(())
            }
            StopRuntimeOutcome::Forced => {
                log_warn("[nex] quit required forced process termination");
                Ok(())
            }
            StopRuntimeOutcome::Failed => Err(RuntimeError::Overlay(
                "quit failed: runtime is still active after graceful and forced attempts"
                    .to_string(),
            )),
        }
    }

    #[cfg(not(target_os = "windows"))]
    {
        log_info("[nex] quit is unsupported on this platform");
        Ok(())
    }
}

pub(crate) fn command_restart() -> Result<(), RuntimeError> {
    #[cfg(target_os = "windows")]
    {
        match stop_runtime_instance(std::time::Duration::from_secs(3))? {
            StopRuntimeOutcome::Failed => {
                return Err(RuntimeError::Overlay(
                    "restart failed: existing runtime could not be stopped".to_string(),
                ));
            }
            StopRuntimeOutcome::Forced => {
                log_warn("[nex] restart required forced process termination");
            }
            StopRuntimeOutcome::Graceful | StopRuntimeOutcome::AlreadyStopped => {}
        }
        run_with_options(RuntimeOptions::default())
    }

    #[cfg(not(target_os = "windows"))]
    {
        run_with_options(RuntimeOptions::default())
    }
}