use korrect::ToK8sStr;
use korrect::cache::parse_version;
use korrect::config::DownloadMode::{Always, Ask, Never};
use korrect::config::VersionStrategy::{Exact, Latest};

use korrect::test_utils::init_logger;

mod integration_utils;
use integration_utils::{
    assert_on_cache, assert_on_korrect_list, assert_on_outputs, mock_kubeconfig, run_cmd_with_env,
    setup_korrect, start_mock_binary_server,
};

#[test]
fn respect_never_download_mode_kubectl() {
    init_logger();
    let test_env = setup_korrect(None, Some(Never), Some(Latest));
    let output = run_cmd_with_env("kubectl version", &test_env, None);
    assert!(!output.status.success());

    let korrect_bin_path = &test_env.home.join(".korrect").join("bin");
    assert_on_outputs(
        output,
        Some(format!(
            "No compatible kubectl found and downloads are disabled. Either manually place a kubectl binary in [{}]",
            korrect_bin_path.to_str().unwrap()
        )),
        None,
    );

    let cache_file = &test_env.cache.join("korrect/kubectl");
    assert!(
        !cache_file.exists(),
        "{} must not exist",
        cache_file.display()
    );
}

#[test]
fn respect_never_download_mode_helm() {
    init_logger();
    let test_env = setup_korrect(None, Some(Never), Some(Latest));
    let output = run_cmd_with_env("helm version", &test_env, None);

    assert!(!output.status.success());

    let korrect_bin_path = test_env.home.join(".korrect").join("bin");
    assert_on_outputs(
        output,
        Some(format!(
            "No compatible helm found and downloads are disabled. Either manually place a helm binary in [{}]",
            korrect_bin_path.to_str().unwrap()
        )),
        None,
    );

    let cache_file = &test_env.cache.join("korrect/helm");
    assert!(
        !cache_file.exists(),
        "{} must not exist",
        cache_file.display()
    );
}

#[test]
fn respect_ask_download_mode_kubectl() {
    init_logger();
    let test_env = setup_korrect(None, Some(Ask), Some(Latest));

    let output = run_cmd_with_env("kubectl version", &test_env, None);

    assert!(!output.status.success());

    assert_on_outputs(
        output,
        Some(format!("No target binary available.",)),
        Some(format!("Download the latest stable kubectl? [y/N]",)),
    );

    let cache_file = &test_env.cache.join("korrect/kubectl");
    assert!(
        !cache_file.exists(),
        "{} must not exist",
        cache_file.display()
    );
}

#[test]
fn respect_ask_download_mode_helm() {
    init_logger();
    let test_env = setup_korrect(None, Some(Ask), Some(Latest));

    let output = run_cmd_with_env("helm version", &test_env, None);

    assert!(!output.status.success());

    assert_on_outputs(
        output,
        Some(format!("No target binary available.")),
        Some(format!("Download the latest stable helm? [y/N]",)),
    );

    let cache_file = &test_env.cache.join("korrect/kubectl");
    assert!(
        !cache_file.exists(),
        "{} must not exist",
        cache_file.display()
    );
}

#[test]
fn respect_exact_version_strategy_hot_cache() {
    init_logger();
    let cluster_version = parse_version("v1.32.123").unwrap();
    let kubectl_version = parse_version("v1.32.4").unwrap();
    let helm_version = parse_version("v3.17.5").unwrap();
    let test_env = setup_korrect(None, Some(Always), Some(Exact))
        .with_hot_cache(mock_kubeconfig(), &cluster_version);

    let server = start_mock_binary_server(
        Some(&kubectl_version.to_k8s_str()),
        Some(&helm_version.to_k8s_str()),
        Some(&vec![kubectl_version.clone()]),
    );
    // fs::write(
    //     PathBuf::from_str("/tmp/url.txt").unwrap(),
    //     format!("SERVER URL {}", server.url()),
    // );
    // log::warn!("SERVER URL {}", server.url());
    // sleep(std::time::Duration::new(600, 0));
    let output = run_cmd_with_env("kubectl version", &test_env, Some(server.url()));
    assert!(output.status.success());

    assert_on_outputs(
        output,
        None,
        Some(format!(
            "I am [kubectl-{}] a mock Kubectl and Helm binary!",
            cluster_version.to_k8s_str()
        )),
    );

    let list_items = &[
        "helm",
        "k",
        "kubectl",
        &format!("kubectl-{}", cluster_version.to_k8s_str()),
    ];
    assert_on_korrect_list(&test_env.home, list_items);

    let output = run_cmd_with_env("helm version", &test_env, Some(server.url()));
    assert!(output.status.success());

    assert_on_outputs(
        output,
        None,
        Some(format!(
            "I am [helm-{}] a mock Kubectl and Helm binary!",
            helm_version.to_k8s_str()
        )),
    );

    let list_items = &[
        "helm",
        &format!("helm-{}", helm_version.to_k8s_str()),
        "k",
        "kubectl",
        &format!("kubectl-{}", cluster_version.to_k8s_str()),
    ];
    assert_on_korrect_list(&test_env.home, list_items);
}

#[test]
fn respect_latest_version_strategy_hot_cache() {
    init_logger();
    let cluster_version = parse_version("v1.30.123").unwrap();
    let latest_kubectl_version = parse_version("v1.33.123").unwrap();
    let expected_kubectl_version = parse_version("v1.31.5").unwrap();
    let helm_version = parse_version("v3.17.5").unwrap();

    let test_env = setup_korrect(None, Some(Always), Some(Latest))
        .with_hot_cache(mock_kubeconfig(), &cluster_version);

    let server = start_mock_binary_server(
        Some(&latest_kubectl_version.to_k8s_str()),
        Some(&helm_version.to_k8s_str()),
        Some(&vec![expected_kubectl_version.clone()]),
    );

    let output = run_cmd_with_env("kubectl version", &test_env, Some(server.url()));
    assert!(output.status.success());
    assert_on_outputs(
        output,
        None,
        Some(format!(
            "I am [kubectl-{}] a mock Kubectl and Helm binary!",
            expected_kubectl_version.to_k8s_str()
        )),
    );

    let list_items = &[
        "helm",
        "k",
        "kubectl",
        &format!("kubectl-{}", expected_kubectl_version.to_k8s_str()),
    ];
    assert_on_korrect_list(&test_env.home, list_items);

    let output = run_cmd_with_env("helm version", &test_env, Some(server.url()));
    assert!(output.status.success());
    assert_on_outputs(
        output,
        None,
        Some(format!(
            "I am [helm-{}] a mock Kubectl and Helm binary!",
            helm_version.to_k8s_str()
        )),
    );

    let list_items = &[
        "helm",
        &format!("helm-{}", helm_version.to_k8s_str()),
        "k",
        "kubectl",
        &format!("kubectl-{}", expected_kubectl_version.to_k8s_str()),
    ];
    assert_on_korrect_list(&test_env.home, list_items);
}

#[test]
fn respect_latest_version_incompatible_strategy_hot_cache() {
    init_logger();
    let cluster_version = parse_version("v1.28.12").unwrap();
    let compatible_kubectl_version = parse_version("v1.29.2").unwrap();
    let latest_kubectl_version = parse_version("v1.32.5").unwrap();
    let helm_version = parse_version("v3.16.5").unwrap();

    let test_env = setup_korrect(None, Some(Always), Some(Latest))
        .with_hot_cache(mock_kubeconfig(), &cluster_version);

    let server = start_mock_binary_server(
        Some(&latest_kubectl_version.to_k8s_str()),
        Some(&helm_version.to_k8s_str()),
        None,
    );

    let output = run_cmd_with_env("kubectl version", &test_env, Some(server.url()));
    assert!(output.status.success());
    assert_on_outputs(
        output,
        None,
        Some(format!(
            "I am [kubectl-{}] a mock Kubectl and Helm binary!",
            compatible_kubectl_version.to_k8s_str()
        )),
    );

    let list_items = &[
        "helm",
        "k",
        "kubectl",
        &format!("kubectl-{}", compatible_kubectl_version.to_k8s_str()),
    ];
    assert_on_korrect_list(&test_env.home, list_items);

    let output = run_cmd_with_env("helm version", &test_env, Some(server.url()));
    assert!(output.status.success());
    assert_on_outputs(
        output,
        None,
        Some(format!(
            "I am [helm-{}] a mock Kubectl and Helm binary!",
            helm_version.to_k8s_str()
        )),
    );

    let list_items = &[
        "helm",
        &format!("helm-{}", helm_version.to_k8s_str()),
        "k",
        "kubectl",
        &format!("kubectl-{}", compatible_kubectl_version.to_k8s_str()),
    ];
    assert_on_korrect_list(&test_env.home, list_items);
}

#[test]
#[ignore = "We need to mock the K8s API server in order to suppor this test case."]
fn respect_exact_version_strategy_cold_cache() {
    init_logger();
    let _test_env = setup_korrect(None, Some(Always), Some(Exact));
}

#[test]
fn test_mock_kubectl_download_no_kubeconfig() {
    init_logger();
    let test_env = setup_korrect(None, None, None);

    let server = start_mock_binary_server(None, None, None);
    let output = run_cmd_with_env("kubectl version", &test_env, Some(server.url()));

    assert!(output.status.success());
    assert_on_outputs(output, None, Some("Korrect-shim Version:".to_string()));
    assert_on_cache(&test_env.cache.join("korrect/kubectl"));

    let list_items = &["helm", "k", "kubectl", "kubectl-v1.33.1"];
    assert_on_korrect_list(&test_env.home, list_items);
}

#[test]
fn test_mock_helm_download_no_kubeconfig() {
    init_logger();
    let test_env = setup_korrect(None, None, None);
    let server = start_mock_binary_server(None, None, None);
    let output = run_cmd_with_env("helm version", &test_env, Some(server.url()));

    assert!(output.status.success());
    assert_on_outputs(output, None, Some("Korrect-shim Version:".to_string()));
    assert_on_cache(&test_env.cache.join("korrect/helm"));

    let list_items = &["helm", "helm-v3.18.2", "k", "kubectl"];
    assert_on_korrect_list(&test_env.home, list_items);
}

#[test]
fn test_invalid_korrect_config() {
    init_logger();
    let test_env = setup_korrect(None, None, None);

    let server = start_mock_binary_server(None, None, None);
    test_env.with_config_file("some junk");
    let output = run_cmd_with_env("kubectl version", &test_env, Some(server.url()));

    assert!(!output.status.success());
    assert_on_outputs(
        output,
        Some("Failed to load configuration".to_string()),
        None,
    );
}

#[test]
fn test_outdated_korrect_config() {
    init_logger();
    let test_env = setup_korrect(None, None, None);
    let server = start_mock_binary_server(None, None, None);
    test_env.with_config_file(
        r#"version = 0
        [korrect-shim]
        download_mode = "Always"
        version_strategy = "Exact"
        manage_helm = true"#,
    );
    let output = run_cmd_with_env("kubectl version", &test_env, Some(server.url()));

    assert!(!output.status.success());
    assert_on_outputs(
        output,
        Some("Unsupported config version: 0".to_string()),
        None,
    );
}

#[test]
fn test_invalid_download_url() {
    init_logger();
    let test_env = setup_korrect(None, None, None);

    let output = run_cmd_with_env("kubectl version", &test_env, Some("not_a_url".to_string()));

    assert!(!output.status.success());
    assert_on_outputs(
        output,
        Some("Korrect failed to fetch version from".to_string()),
        None,
    );
}

#[test]
fn test_unresolvable_download_url() {
    init_logger();
    let test_env = setup_korrect(None, None, None);

    let output = run_cmd_with_env(
        "kubectl version",
        &test_env,
        Some("http://something.invalid".to_string()),
    );

    assert!(!output.status.success());
    assert_on_outputs(
        output,
        Some("Korrect failed to fetch version from".to_string()),
        None,
    );
}
