// Auto-generated build.rs for {{project_name}}
// Generated by `metabots init`
//
// This build script:
// 1. Generates node registry from metabots.json (filtered by run target)
// 2. Embeds configuration files and models
// 3. Validates embedded resources exist

use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use serde_json::Value;

fn main() {
    println!("cargo:rerun-if-changed=metabots.json");
    println!("cargo:rerun-if-changed=embedded/");

    // 1. Generate node registry from metabots.json
    generate_node_registry();

    // 2. Generate embedded asset accessors
    generate_embedded_assets();

    // 3. Validate all embedded resources exist
    validate_embedded_resources();
}

fn generate_node_registry() {
    let manifest_str = fs::read_to_string("metabots.json")
        .expect("Failed to read metabots.json");
    let manifest: Value = serde_json::from_str(&manifest_str)
        .expect("Failed to parse metabots.json");

    let mut code = String::from("// Auto-generated from metabots.json\n");
    code.push_str("// Filtered by run target via Cargo features\n\n");

    // Detect which features are enabled to determine run target
    let is_robot = env::var("CARGO_FEATURE_TARGET_ROBOT").is_ok();
    let is_remote = env::var("CARGO_FEATURE_TARGET_REMOTE").is_ok();
    let is_dev = env::var("CARGO_FEATURE_TARGET_DEV").is_ok();

    let run_target = if is_dev {
        "dev"
    } else if is_robot {
        "robot"
    } else if is_remote {
        "remote"
    } else {
        "dev" // fallback
    };

    code.push_str(&format!("// Compiled for run target: {}\n\n", run_target));

    // Helper to check if node should be included
    let should_include = |node_target: &str| -> bool {
        match run_target {
            "dev" => true, // Include all nodes in dev mode
            "robot" => node_target == "robot" || node_target == "shared",
            "remote" => node_target == "remote" || node_target == "shared",
            _ => false,
        }
    };

    // Generate enabled nodes list
    code.push_str("pub const ENABLED_NODES: &[&str] = &[\n");

    if let Some(drivers) = manifest["nodes"]["drivers"].as_array() {
        for driver in drivers {
            if driver["enabled"].as_bool().unwrap_or(false) {
                let name = driver["name"].as_str().unwrap();
                let node_target = driver["run_target"].as_str().unwrap_or("robot");

                if should_include(node_target) {
                    code.push_str(&format!("    \"{}\",\n", name));
                }
            }
        }
    }

    if let Some(custom) = manifest["nodes"]["custom"].as_array() {
        for node in custom {
            if node["enabled"].as_bool().unwrap_or(false) {
                let name = node["name"].as_str().unwrap();
                let node_target = node["run_target"].as_str().unwrap_or("robot");

                if should_include(node_target) {
                    code.push_str(&format!("    \"{}\",\n", name));
                }
            }
        }
    }

    code.push_str("];\n\n");

    // Add run target constant
    code.push_str(&format!("pub const RUN_TARGET: &str = \"{}\";\n\n", run_target));

    // Write to OUT_DIR
    let out_dir = env::var("OUT_DIR").unwrap();
    let dest_path = Path::new(&out_dir).join("node_registry.rs");
    fs::write(&dest_path, code).expect("Failed to write node_registry.rs");

    println!("cargo:warning=Generated node registry for run target: {}", run_target);
}

fn generate_embedded_assets() {
    let mut code = String::from("// Auto-generated embedded assets\n\n");
    code.push_str("pub struct Embedded;\n\n");
    code.push_str("impl Embedded {\n");

    // Embed all configs
    code.push_str("    pub fn config(path: &str) -> Option<&'static str> {\n");
    code.push_str("        match path {\n");

    if Path::new("embedded/configs").exists() {
        for entry in find_files("embedded/configs", &["json", "yaml", "yml"]) {
            let relative = entry.strip_prefix("embedded/configs/")
                .unwrap()
                .to_str()
                .unwrap()
                .replace('\\', "/");
            code.push_str(&format!(
                "            \"{}\" => Some(include_str!(\"../embedded/configs/{}\")),\n",
                relative, relative
            ));
        }
    }

    code.push_str("            _ => None,\n");
    code.push_str("        }\n");
    code.push_str("    }\n\n");

    // Embed all models
    code.push_str("    pub fn model(name: &str) -> Option<&'static [u8]> {\n");
    code.push_str("        match name {\n");

    if Path::new("embedded/models").exists() {
        for entry in find_files("embedded/models", &["onnx", "pt", "tflite"]) {
            let name = entry.file_stem().unwrap().to_str().unwrap();
            let relative = entry.strip_prefix("embedded/models/")
                .unwrap()
                .to_str()
                .unwrap()
                .replace('\\', "/");
            code.push_str(&format!(
                "            \"{}\" => Some(include_bytes!(\"../embedded/models/{}\")),\n",
                name, relative
            ));
        }
    }

    code.push_str("            _ => None,\n");
    code.push_str("        }\n");
    code.push_str("    }\n\n");

    // List all available configs
    code.push_str("    pub fn list_configs() -> &'static [&'static str] {\n");
    code.push_str("        &[\n");

    if Path::new("embedded/configs").exists() {
        for entry in find_files("embedded/configs", &["json", "yaml", "yml"]) {
            let relative = entry.strip_prefix("embedded/configs/")
                .unwrap()
                .to_str()
                .unwrap()
                .replace('\\', "/");
            code.push_str(&format!("            \"{}\",\n", relative));
        }
    }

    code.push_str("        ]\n");
    code.push_str("    }\n\n");

    // List all available models
    code.push_str("    pub fn list_models() -> &'static [&'static str] {\n");
    code.push_str("        &[\n");

    if Path::new("embedded/models").exists() {
        for entry in find_files("embedded/models", &["onnx", "pt", "tflite"]) {
            let name = entry.file_stem().unwrap().to_str().unwrap();
            code.push_str(&format!("            \"{}\",\n", name));
        }
    }

    code.push_str("        ]\n");
    code.push_str("    }\n");

    code.push_str("}\n");

    // Write to OUT_DIR
    let out_dir = env::var("OUT_DIR").unwrap();
    let dest_path = Path::new(&out_dir).join("embedded.rs");
    fs::write(&dest_path, code).expect("Failed to write embedded.rs");
}

fn find_files<P: AsRef<Path>>(dir: P, extensions: &[&str]) -> Vec<PathBuf> {
    let mut files = Vec::new();

    if let Ok(entries) = fs::read_dir(dir) {
        for entry in entries.flatten() {
            let path = entry.path();

            if path.is_dir() {
                files.extend(find_files(&path, extensions));
            } else if let Some(ext) = path.extension() {
                if extensions.iter().any(|&e| e == ext) {
                    files.push(path);
                }
            }
        }
    }

    files
}

fn validate_embedded_resources() {
    let manifest_str = fs::read_to_string("metabots.json")
        .expect("Failed to read metabots.json");
    let manifest: Value = serde_json::from_str(&manifest_str)
        .expect("Failed to parse metabots.json");

    // Validate that embedded/ directory exists
    if !Path::new("embedded").exists() {
        fs::create_dir_all("embedded")
            .expect("Failed to create embedded directory");
        println!("cargo:warning=Created embedded/ directory");
    }

    // Validate configs directory
    if !Path::new("embedded/configs").exists() {
        fs::create_dir_all("embedded/configs")
            .expect("Failed to create embedded/configs directory");
        println!("cargo:warning=Created embedded/configs directory");
    }

    // Validate models directory
    if !Path::new("embedded/models").exists() {
        fs::create_dir_all("embedded/models")
            .expect("Failed to create embedded/models directory");
        println!("cargo:warning=Created embedded/models directory");
    }

    // Validate infrastructure configs exist
    let required_infra_configs = ["development", "local", "production"];
    for env in &required_infra_configs {
        let path = format!("embedded/configs/infrastructure/infrastructure.{}.yaml", env);
        if !Path::new(&path).exists() {
            println!("cargo:warning=Missing infrastructure config: {}", path);
            println!("cargo:warning=Run `metabots config generate` to create default configs");
        }
    }
}
