//! Build script.
//!
//! Reads `.env` and bakes any `POLYMODEL_*` variables into `src/env.rs` as
//! compile-time constants. This is how configuration reaches the WASM bundle,
//! which has no filesystem or process environment to read at runtime.
//!
//! Server-side runtime configuration (DATABASE_URL, HYDRANT_*) is NOT handled
//! here: the server binary loads `.env` directly via `dotenvy` and reads it into
//! a typed `ServerConfig` struct (see `src/indexing/config.rs`).

use serde_json::Value;
use std::env;
use std::fs::{self, File};
use std::io::Write;
use std::path::{Path, PathBuf};

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

    dotenvy::dotenv().ok();

    let mut f = File::create("./src/env.rs").expect("create src/env.rs");
    f.write_all(
        b"// This file is automatically generated by build.rs.\n\
          // Frontend (WASM) compile-time configuration.\n\
          // Do not edit; regenerate by rebuilding.\n",
    )
    .expect("write env.rs header");

    // Bake every POLYMODEL_* env var into the bundle as a `&'static str` const.
    for (key, value) in env::vars() {
        if let Some(rest) = key.strip_prefix("POLYMODEL_") {
            if rest.is_empty() {
                continue;
            }
            let line = format!(
                "#[allow(unused)]\npub const {}: &str = \"{}\";\n",
                key,
                value.replace('\\', "\\\\").replace('"', "\\\"")
            );
            f.write_all(line.as_bytes()).expect("write env.rs const");
        }
    }
    generate_indexed_collections();
}
fn generate_indexed_collections() {
    println!("cargo:rerun-if-changed=lexicons");
    let lexicon_root = Path::new("lexicons");
    let mut json_paths = Vec::new();
    collect_json_paths(lexicon_root, &mut json_paths);
    json_paths.sort();
    let mut collections = Vec::new();
    for path in json_paths {
        println!("cargo:rerun-if-changed={}", path.display());
        let contents = fs::read_to_string(&path)
            .unwrap_or_else(|e| panic!("read authored lexicon {}: {e}", path.display()));
        let lexicon: Value = serde_json::from_str(&contents)
            .unwrap_or_else(|e| panic!("parse authored lexicon JSON {}: {e}", path.display()));
        if lexicon.pointer("/defs/main/type").and_then(Value::as_str) != Some("record") {
            continue;
        }
        let id = lexicon
            .get("id")
            .and_then(Value::as_str)
            .unwrap_or_else(|| panic!("record lexicon {} has no string id", path.display()));
        if id.starts_with("space.polymodel.") {
            collections.push(id.to_owned());
        }
    }
    collections.sort();
    let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR set by Cargo"));
    let out_path = out_dir.join("polymodel_record_collections.rs");
    let mut out = File::create(&out_path)
        .unwrap_or_else(|e| panic!("create generated collections {}: {e}", out_path.display()));
    writeln!(out, "// Generated by build.rs from authored lexicons where defs.main.type == \"record\".\n// Do not edit.\npub const INDEXED_COLLECTIONS: &[&str] = &[").expect("write generated collections header");
    for collection in collections {
        writeln!(out, "    {collection:?},").expect("write generated collection");
    }
    writeln!(out, "];").expect("write generated collections footer");
}
fn collect_json_paths(dir: &Path, paths: &mut Vec<PathBuf>) {
    let entries =
        fs::read_dir(dir).unwrap_or_else(|e| panic!("read lexicon dir {}: {e}", dir.display()));
    for entry in entries {
        let entry =
            entry.unwrap_or_else(|e| panic!("read lexicon dir entry in {}: {e}", dir.display()));
        let path = entry.path();
        if path.is_dir() {
            collect_json_paths(&path, paths);
        } else if path.extension().and_then(|ext| ext.to_str()) == Some("json") {
            paths.push(path);
        }
    }
}
