use std::fs;
use std::fs::File;
use std::io::Write;
use std::path::Path;

fn to_safe_identifier(name: &str) -> String {
  let mut safe = String::new();
  for (i, c) in name.chars().enumerate() {
    if c.is_uppercase() {
      if i > 0 {
        safe.push('_');
      }
      safe.push(c.to_ascii_lowercase());
    } else if c == '-' || c == '.' {
      safe.push('_');
    } else {
      safe.push(c);
    }
  }
  safe
}

fn main() {
  println!("cargo:rerun-if-changed=fixtures");
  println!("cargo:rerun-if-changed=Cargo.toml");

  // File: src/tests/fixtures.rs for standard #[test] integration
  let std_dest_path = Path::new("src/tests/fixtures.rs");
  let mut std_file = File::create(std_dest_path).unwrap();

  // Walk tests/fixtures directly
  let fixtures_path = "fixtures";
  if Path::new(fixtures_path).exists() {
    for entry in fs::read_dir(fixtures_path).unwrap() {
      let entry = entry.unwrap();
      let path = entry.path();
      if path.extension().unwrap_or_default() == "json" {
        let file_name = path.file_stem().unwrap().to_str().unwrap();

        // Parse the JSON file to find blocks
        let file = File::open(&path).unwrap();
        let val: serde_json::Value = serde_json::from_reader(file).unwrap();

        if let Some(arr) = val.as_array() {
          for (i, item) in arr.iter().enumerate() {
            // Enforce test suite structure
            let group = item.as_object().expect("Test suite must be an object");

            // Validate required suite fields
            if !group.contains_key("description")
              || !group.contains_key("database")
              || !group.contains_key("tests")
            {
              panic!(
                "File {} index {} is missing required suite fields (description, database, tests)",
                file_name, i
              );
            }

            // Validate required test case fields
            let tests = group
              .get("tests")
              .unwrap()
              .as_array()
              .expect("Tests must be an array");
            let safe_filename = to_safe_identifier(file_name);
            for (t_idx, test) in tests.iter().enumerate() {
              let t_obj = test.as_object().expect("Test case must be an object");
              if !t_obj.contains_key("description") {
                panic!(
                  "File {} suite {} test {} is missing required case fields (description)",
                  file_name, i, t_idx
                );
              }

              // Use deterministic names: test_{filename}_{suite_idx}_{test_idx}
              let fn_name = format!("test_{}_{}_{}", safe_filename, i, t_idx);

              // Write to src/tests/fixtures.rs (Std Test)
              write!(
                std_file,
                r#"
#[test]
fn {}() {{
    let path = format!("{{}}/fixtures/{}.json", env!("CARGO_MANIFEST_DIR"));
    crate::tests::runner::run_test_case(&path, {}, {}).unwrap();
}}
"#,
                fn_name, file_name, i, t_idx
              )
              .unwrap();
            }
          }
        }
      }
    }
  }
}
