use crate::{codegen, config_io, parser};
use anyhow::{Context, Result};
use std::env;
use std::fs;
use std::path::{Path, PathBuf};

/// Helper for `build.rs` to integrate Anaxa configuration.
pub struct BuildHelper {
    kconfig_dir: PathBuf,
    config_file: PathBuf,
    out_dir: PathBuf,
}

impl BuildHelper {
    pub fn new() -> Result<Self> {
        let out_dir = env::var_os("OUT_DIR").context("OUT_DIR not set")?.into();
        Ok(Self {
            kconfig_dir: PathBuf::from("src"),
            config_file: PathBuf::from(".config"),
            out_dir,
        })
    }

    pub fn with_kconfig_dir<P: Into<PathBuf>>(mut self, dir: P) -> Self {
        self.kconfig_dir = dir.into();
        self
    }

    pub fn with_config_file<P: Into<PathBuf>>(mut self, file: P) -> Self {
        self.config_file = file.into();
        self
    }

    pub fn build(self) -> Result<()> {
        check_version_compatibility()?;
        let tree = parser::build_config_tree(&self.kconfig_dir)?;
        let configs = parser::flatten_configs(&tree);
        parser::validate_configs(&configs)?;
        let values = config_io::load_config(&self.config_file, &configs)?;

        let out_path = self.out_dir.join("config.rs");
        let rust_code = codegen::rust::generate_consts(&configs, &values)?;
        fs::write(&out_path, rust_code)
            .with_context(|| format!("Failed to write to {:?}", out_path))?;

        println!("cargo:rerun-if-changed={}", self.config_file.display());
        emit_rerun_if_changed(&self.kconfig_dir)?;

        for item in &configs {
            if let Some(val) = values.get(&item.name) {
                if val.as_bool() == Some(true) {
                    println!("cargo:rustc-cfg={}", item.name);
                } else if val.as_bool() == None {
                    continue;
                }
                println!("cargo::rustc-check-cfg=cfg({})", item.name);
            }
        }

        for (k, v) in values {
            let v_str = match v {
                toml::Value::String(s) => s,
                toml::Value::Integer(i) => i.to_string(),
                toml::Value::Float(f) => f.to_string(),
                toml::Value::Boolean(b) => b.to_string(),
                _ => continue,
            };
            println!("cargo:rustc-env=ANAXA_{}={}", k.to_uppercase(), v_str);
        }

        Ok(())
    }
}

/// Helper for `build.rs` to integrate Anaxa configuration.
///
/// This function:
/// 1. Scans `kconfig_dir` for `Kconfig.toml` files.
/// 2. Loads configuration values from `config_file`.
/// 3. Generates `config.rs` in `OUT_DIR`.
/// 4. Emits `cargo:rustc-cfg` for enabled boolean configs.
/// 5. Emits `cargo:rerun-if-changed` for the config file and all `Kconfig.toml` files.
pub fn emit_cargo_instructions<P1, P2>(kconfig_dir: P1, config_file: P2) -> Result<()>
where
    P1: AsRef<Path>,
    P2: AsRef<Path>,
{
    check_version_compatibility()?;
    let kconfig_dir = kconfig_dir.as_ref();
    let config_file = config_file.as_ref();

    let tree = parser::build_config_tree(kconfig_dir)?;
    let configs = parser::flatten_configs(&tree);
    parser::validate_configs(&configs)?;

    let values = config_io::load_config(config_file, &configs)?;

    let out_dir = env::var_os("OUT_DIR").context("OUT_DIR not set")?;
    let out_path = PathBuf::from(out_dir).join("config.rs");
    let rust_code = codegen::rust::generate_consts(&configs, &values)?;
    fs::write(&out_path, rust_code)
        .with_context(|| format!("Failed to write to {:?}", out_path))?;

    println!("cargo:rerun-if-changed={}", config_file.display());

    emit_rerun_if_changed(kconfig_dir)?;

    for item in &configs {
        if let Some(val) = values.get(&item.name) {
            if val.as_bool() == Some(true) {
                println!("cargo:rustc-cfg={}", item.name);
            }
        }
    }

    Ok(())
}

fn emit_rerun_if_changed(dir: &Path) -> Result<()> {
    use walkdir::WalkDir;
    for entry in WalkDir::new(dir).into_iter().filter_map(|e| e.ok()) {
        if entry.file_name() == "Kconfig.toml" {
            println!("cargo:rerun-if-changed={}", entry.path().display());
        }
    }
    Ok(())
}

fn check_version_compatibility() -> Result<()> {
    if let Ok(cli_version) = env::var("ANAXA_CLI_VERSION") {
        let lib_version = env!("CARGO_PKG_VERSION");
        if cli_version != lib_version {
            // Check if major/minor versions match
            let cli_parts: Vec<&str> = cli_version.split('.').collect();
            let lib_parts: Vec<&str> = lib_version.split('.').collect();

            if cli_parts.len() >= 2 && lib_parts.len() >= 2 {
                if cli_parts[0] != lib_parts[0] || cli_parts[1] != lib_parts[1] {
                    println!(
                        "cargo:warning=Version mismatch: anaxa-builder CLI (v{}) and library (v{}) are not compatible.",
                        cli_version, lib_version
                    );
                    println!(
                        "cargo:warning=Please ensure both CLI and library have the same major.minor version."
                    );
                }
            } else if cli_version != lib_version {
                println!(
                    "cargo:warning=Version mismatch detected: CLI v{}, Lib v{}",
                    cli_version, lib_version
                );
            }
        }
    }
    Ok(())
}
