use std::path::PathBuf;

use assert_fs::prelude::PathCopy;
use flaky_test::flaky_test;
use lux_lib::{
    config::ConfigBuilder, lua_installation::detect_installed_lua_version, lua_version::LuaVersion,
    operations::Test, workspace::Workspace,
};
use tokio::fs::remove_dir_all;

#[flaky_test(tokio, times = 5)]
async fn run_busted_test() {
    let project_root =
        PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("resources/test/sample-projects/busted/");
    let temp_dir = assert_fs::TempDir::new().unwrap();
    temp_dir.copy_from(&project_root, &["**"]).unwrap();
    let workspace_root = temp_dir.path();
    let workspace = Workspace::from_exact(workspace_root).unwrap().unwrap();
    let tree_root = workspace.root().to_path_buf().join(".lux");
    let _ = remove_dir_all(&tree_root).await;

    let lua_version = detect_installed_lua_version().or(Some(LuaVersion::Lua51));

    let config = ConfigBuilder::new()
        .unwrap()
        .user_tree(Some(tree_root))
        .lua_version(lua_version)
        .build()
        .unwrap();

    Test::new(workspace, &config).run().await.unwrap();
}

#[flaky_test(tokio, times = 5)]
async fn run_busted_test_no_lock() {
    let project_root =
        PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("resources/test/sample-projects/busted/");
    let temp_dir = assert_fs::TempDir::new().unwrap();
    temp_dir.copy_from(&project_root, &["**"]).unwrap();
    let workspace_root = temp_dir.path();
    let workspace = Workspace::from_exact(workspace_root).unwrap().unwrap();
    let tree_root = workspace.root().to_path_buf().join(".lux");
    let _ = remove_dir_all(&tree_root).await;

    let lua_version = detect_installed_lua_version().or(Some(LuaVersion::Lua51));

    let config = ConfigBuilder::new()
        .unwrap()
        .user_tree(Some(tree_root))
        .lua_version(lua_version)
        .build()
        .unwrap();

    Test::new(workspace, &config)
        .no_lock(true)
        .run()
        .await
        .unwrap();
}

#[cfg(not(target_os = "windows"))]
#[flaky_test(tokio, times = 5)]
async fn non_regression_lockfile_corruption() {
    let sample_project_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("resources/test/sample-projects/busted-with-lockfile/");
    let _ = remove_dir_all(sample_project_dir.join(".lux")).await;
    let temp_dir = assert_fs::TempDir::new().unwrap();
    temp_dir.copy_from(sample_project_dir, &["**"]).unwrap();
    let project = Workspace::from_exact(temp_dir.path()).unwrap().unwrap();
    let config = ConfigBuilder::new().unwrap().build().unwrap();

    let lockfile_before_test =
        String::from_utf8(tokio::fs::read(project.lockfile_path()).await.unwrap());

    Test::new(project, &config)
        .no_lock(true)
        .run()
        .await
        .unwrap();

    let workspace = Workspace::from_exact(temp_dir.path()).unwrap().unwrap();
    let lockfile_after_test =
        String::from_utf8(tokio::fs::read(workspace.lockfile_path()).await.unwrap());
    assert_eq!(lockfile_before_test, lockfile_after_test);
}

#[cfg(not(target_env = "msvc"))]
#[flaky_test(tokio, times = 5)]
async fn run_busted_nlua_test() {
    run_busted_nlua_test_impl(false).await
}

#[cfg(not(target_env = "msvc"))]
#[flaky_test(tokio, times = 5)]
async fn run_busted_nlua_test_no_lock() {
    run_busted_nlua_test_impl(true).await
}

#[cfg(not(target_env = "msvc"))]
#[cfg(test)]
async fn run_busted_nlua_test_impl(no_lock: bool) {
    let project_root = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("resources/test/sample-projects/busted-nlua/");
    let temp_dir = assert_fs::TempDir::new().unwrap();
    temp_dir.copy_from(&project_root, &["**"]).unwrap();
    let workspace_root = temp_dir.path();
    let workspace = Workspace::from_exact(workspace_root).unwrap().unwrap();
    let tree_root = workspace.root().to_path_buf().join(".lux");
    let _ = remove_dir_all(&tree_root).await;

    let config = ConfigBuilder::new()
        .unwrap()
        .user_tree(Some(tree_root))
        .build()
        .unwrap();
    Test::new(workspace, &config)
        .no_lock(no_lock)
        .run()
        .await
        .unwrap();
}
