#![allow(missing_docs)]

use std::fs;
use std::path::PathBuf;

use cfg_expr::targets::get_builtin_target_by_triple;
use pretty_assertions::assert_str_eq;

use cargo_toml_edit::{DependencyFilter, FeatureFilter, Manifest};

const NORMALIZE_FILES: [&str; 6] = [
    "bzip2-sys-0.1.11",
    "curl-sys-0.4.66",
    "libgit2-sys-0.16.1",
    "zstd-sys-2.0.8",
    // noop test cases
    "serde-1.0.196",
    "syn-2.0.34",
];

const STRIP_FILES: [&str; 17] = [
    "anstream-0.5.0",
    "backtrace-0.3.69",
    "bytes-1.5.0",
    "chrono-0.4.31",
    "cpufeatures-0.2.9",
    "gst-plugin-gtk4-0.11.0",
    "indicatif-0.17.7",
    "native-tls-0.2.11",
    "nix-0.27.1",
    "rustix-0.36.8",
    "time-0.3.29",
    "tokio-1.32.0",
    "which-4.4.2",
    "wild-2.2.0",
    "zbus-3.14.1",
    // noop test cases
    "serde-1.0.196",
    "syn-2.0.34",
];

fn old_from_prefix(prefix: &str) -> PathBuf {
    let mut path = PathBuf::new();
    path.push(env!("CARGO_MANIFEST_DIR"));
    path.push("tests/data");
    path.push(format!("{prefix}.Cargo.toml"));
    path
}

fn new_from_prefix(prefix: &str) -> PathBuf {
    let mut path = PathBuf::new();
    path.push(env!("CARGO_MANIFEST_DIR"));
    path.push("tests/data");
    path.push(format!("{prefix}.Cargo.toml.patched"));
    path
}

#[allow(clippy::expect_used)]
fn default_fedora_filter() -> DependencyFilter {
    // default configuration for Fedora
    let target_triples: [&str; 7] = [
        "x86_64-unknown-linux-gnu",
        "i686-unknown-linux-gnu",
        "aarch64-unknown-linux-gnu",
        "armv7-unknown-linux-gnueabihf",
        "powerpc64le-unknown-linux-gnu",
        "s390x-unknown-linux-gnu",
        "riscv64gc-unknown-linux-gnu",
    ];
    let target_triples: Vec<_> = target_triples
        .iter()
        .map(|t| get_builtin_target_by_triple(t).expect("Unknown target triple"))
        .collect();

    let mut filter = DependencyFilter::empty();
    filter.targets = target_triples;

    filter
}

#[test]
fn normalize_version() {
    for prefix in NORMALIZE_FILES {
        println!("Testing: {}", prefix);

        let original_path = old_from_prefix(prefix);
        let expected_path = new_from_prefix(prefix);

        let original_toml = fs::read_to_string(original_path).unwrap();
        let expected_toml = fs::read_to_string(expected_path).unwrap();

        let mut manifest: Manifest = original_toml.parse().unwrap();
        manifest.normalize_version().unwrap();
        let patched_toml = manifest.to_string();

        assert_str_eq!(
            patched_toml,
            expected_toml,
            "Mismatch with expected string for {}",
            prefix
        );
    }
}

#[test]
fn filter_dependencies() {
    for prefix in STRIP_FILES {
        println!("Testing: {}", prefix);

        let original_path = old_from_prefix(prefix);
        let expected_path = new_from_prefix(prefix);

        let original_toml = fs::read_to_string(original_path).unwrap();
        let expected_toml = fs::read_to_string(expected_path).unwrap();

        let mut manifest: Manifest = original_toml.parse().unwrap();
        manifest.filter_dependencies(&default_fedora_filter()).unwrap();
        let patched_toml = manifest.to_string();

        assert_str_eq!(
            patched_toml,
            expected_toml,
            "Mismatch with expected string for {}",
            prefix
        );
    }
}

#[test]
fn filter_features() {
    let feature_input: [(&str, Vec<String>, bool); 1] = [(
        "sequoia-openpgp-2.3.0",
        vec![
            String::from("allow-experimental-crypto"),
            String::from("allow-variable-time-crypto"),
            String::from("crypto-botan2"),
            String::from("crypto-cng"),
            String::from("crypto-rust"),
        ],
        false,
    )];

    for (prefix, features, safe) in feature_input {
        println!("Testing: {}", prefix);

        let original_path = old_from_prefix(prefix);
        let expected_path = new_from_prefix(prefix);

        let original_toml = fs::read_to_string(original_path).unwrap();
        let expected_toml = fs::read_to_string(expected_path).unwrap();

        let mut filter = FeatureFilter::empty();
        filter.features = features;
        filter.unsafe_mode = !safe;

        let mut manifest: Manifest = original_toml.parse().unwrap();
        manifest.filter_features(&filter).unwrap();
        let patched_toml = manifest.to_string();

        assert_str_eq!(
            patched_toml,
            expected_toml,
            "Mismatch with expected string for {}",
            prefix
        );
    }
}

#[test]
fn filter_features_safe_error() {
    let feature_input: [(&str, Vec<String>, bool); 1] =
        [("sequoia-openpgp-2.3.0", vec![String::from("compression")], true)];

    for (prefix, features, safe) in feature_input {
        println!("Testing: {}", prefix);

        let original_path = old_from_prefix(prefix);
        let original_toml = fs::read_to_string(original_path).unwrap();

        let mut filter = FeatureFilter::empty();
        filter.features = features;
        filter.unsafe_mode = !safe;

        let mut manifest: Manifest = original_toml.parse().unwrap();
        let err = manifest.filter_features(&filter).unwrap_err();

        assert!(matches!(err, cargo_toml_edit::Error::NotAllowed { .. }));
    }
}
