use nishikaze::core::cli::Cli;
use nishikaze::core::{cmd, log};

fn run_main_with(cli: Cli, run_fn: fn(&Cli) -> Result<(), nishikaze::core::cmd::KazeError>) -> i32 {
    match run_fn(&cli) {
        Ok(()) => 0,
        Err(e) => {
            log::error(format!("error: {e:?}"));
            1
        }
    }
}

fn run_main() -> i32 {
    let cli = Cli::parse_args();
    let run_fn = resolve_run_fn();
    run_main_with(cli, run_fn)
}

fn main() {
    std::process::exit(run_main());
}

#[cfg(test)]
fn resolve_run_fn() -> fn(&Cli) -> Result<(), nishikaze::core::cmd::KazeError> {
    let guard = TEST_RUN_FN.lock().expect("lock test run fn");
    guard.unwrap_or(cmd::run)
}

#[cfg(not(test))]
fn resolve_run_fn() -> fn(&Cli) -> Result<(), nishikaze::core::cmd::KazeError> {
    cmd::run
}

#[cfg(test)]
type TestRunFn = fn(&Cli) -> Result<(), nishikaze::core::cmd::KazeError>;

#[cfg(test)]
static TEST_RUN_FN: std::sync::Mutex<Option<TestRunFn>> = std::sync::Mutex::new(None);

#[cfg(test)]
mod tests {
    use nishikaze::core::cli::{Command, PhaseArgs};
    use pretty_assertions::assert_eq;

    use super::{TEST_RUN_FN, resolve_run_fn, run_main_with};

    /**
     * Runs `main` to ensure the CLI entry path is covered in tests.
     */
    #[test]
    fn test_main() {
        let cli = nishikaze::core::cli::Cli {
            preclean: false,
            all: false,
            profile: None,
            board: None,
            runner: None,
            project: None,
            verbose: 0,
            dry_run: true,
            command: Command::Profiles,
        };
        {
            let mut guard = TEST_RUN_FN.lock().expect("lock test run fn");
            *guard = Some(|_cli| Ok(()));
        }
        assert_eq!(run_main_with(cli, resolve_run_fn()), 0);
        {
            let mut guard = TEST_RUN_FN.lock().expect("lock test run fn");
            *guard = None;
        }
    }

    #[test]
    fn test_main_error_path() {
        let cli = nishikaze::core::cli::Cli {
            preclean: false,
            all: false,
            profile: None,
            board: None,
            runner: None,
            project: None,
            verbose: 0,
            dry_run: true,
            command: Command::Conf(PhaseArgs::default()),
        };
        {
            let mut guard = TEST_RUN_FN.lock().expect("lock test run fn");
            *guard = Some(|_cli| Err(nishikaze::core::cmd::KazeError::Msg("boom".into())));
        }
        assert_eq!(run_main_with(cli, resolve_run_fn()), 1);
        {
            let mut guard = TEST_RUN_FN.lock().expect("lock test run fn");
            *guard = None;
        }
    }
}
