//! Utils for debugging hg-core

use crate::config::Config;

/// Write the file path given by the config option `devel.<config_option>` with
/// the suffix `.waiting`, then wait for the file path given by the
/// config option `devel.<config_option>` to appear on disk
/// up to `devel.<config_option>-timeout` seconds.
/// Note that the timeout may be higher because we scale it if global
/// `run-tests` timeouts are raised to prevent flakiness on slower hardware.
///
/// Useful for testing race conditions.
pub fn debug_wait_for_file(
    config: &Config,
    config_option: &str,
) -> Result<(), String> {
    let path_opt = format!("sync.{config_option}");
    let file_path = match config.get_str(b"devel", path_opt.as_bytes()).ok() {
        Some(Some(file_path)) => file_path,
        _ => return Ok(()),
    };

    // TODO make it so `configitems` is shared between Rust and Python so that
    // defaults work out of the box, etc.
    let default_timeout = 2;
    let timeout_opt = format!("sync.{config_option}-timeout");
    let timeout_seconds = match config.get_u32(b"devel", timeout_opt.as_bytes())
    {
        Ok(Some(timeout)) => timeout,
        _ => default_timeout,
    };
    let timeout_seconds = timeout_seconds as u64;

    tracing::debug!(
        "Config option `{config_option}` found, \
             waiting for file `{file_path}` to be created"
    );
    std::fs::File::create(format!("{file_path}.waiting")).ok();
    // If the test timeout have been extended, scale the timer relative
    // to the normal timing.
    let global_default_timeout: u64 = std::env::var("HGTEST_TIMEOUT_DEFAULT")
        .map(|t| t.parse())
        .unwrap_or(Ok(0))
        .unwrap();
    let global_timeout_override: u64 = std::env::var("HGTEST_TIMEOUT")
        .map(|t| t.parse())
        .unwrap_or(Ok(0))
        .unwrap();
    let timeout_seconds = if global_default_timeout < global_timeout_override {
        timeout_seconds * global_timeout_override / global_default_timeout
    } else {
        timeout_seconds
    };
    let timeout = std::time::Duration::from_secs(timeout_seconds);

    let start = std::time::Instant::now();
    let path = std::path::Path::new(file_path);
    let mut found = false;
    while start.elapsed() < timeout {
        if path.exists() {
            tracing::debug!("File `{file_path}` was created");
            found = true;
            break;
        } else {
            std::thread::sleep(std::time::Duration::from_millis(10));
        }
    }
    if !found {
        let msg = format!(
            "File `{file_path}` set by `{config_option}` was not found \
            within the allocated {timeout_seconds} seconds timeout"
        );
        Err(msg)
    } else {
        Ok(())
    }
}

pub fn debug_wait_for_file_or_print(config: &Config, config_option: &str) {
    if let Err(e) = debug_wait_for_file(config, config_option) {
        eprintln!("{e}");
    };
}
