use std::fs;

use beancount_importers_generators::install_tracing;
use camino::Utf8Path;
use camino::Utf8PathBuf;
use color_eyre::eyre::WrapErr;
use color_eyre::Result;
use icu_datagen::baked_exporter::BakedExporter;
use icu_datagen::BakedOptions;
use icu_datagen::DatagenDriver;
use icu_datagen::DatagenProvider;
use icu_decimal::provider::DecimalSymbolsV1Marker;
use icu_locid::langid;
use icu_provider::marker::KeyedDataMarker as _;

fn main() -> Result<()> {
    install_tracing();

    color_eyre::install()?;

    let current_checksum = compute_checksum();

    let manifest_path = cargo_px_env::generated_pkg_manifest_path()
        .wrap_err("getting manifest path from ENV")
        .and_then(|path| Utf8PathBuf::try_from(path).wrap_err("parsing manifest path as UTF-8"))?;

    let mut manifest = load_manifest(&manifest_path).wrap_err("loading manifest")?;

    let old_checksum = &mut manifest["package"]["metadata"]["px"]["source"]["checksum"];
    if let Some(old_checksum) = old_checksum.as_str() {
        if old_checksum == current_checksum {
            return Ok(());
        }
    }

    let provider = DatagenProvider::new_latest_tested();

    let mod_directory = {
        let mut path = manifest_path.clone();

        path.pop();
        path.push("src");
        path.push("icu_data");

        path.into_std_path_buf()
    };

    let sink = BakedExporter::new(mod_directory, {
        let mut options = BakedOptions::default();
        options.pretty = true;
        options.use_separate_crates = true;
        options.overwrite = true;

        options
    })
    .wrap_err("building exporter")?;

    DatagenDriver::new()
        .with_keys([DecimalSymbolsV1Marker::KEY])
        .with_locales([langid!("en")])
        .export(&provider, sink)
        .wrap_err("generating ICU data")?;

    // Update checksum
    *old_checksum = toml_edit::value(current_checksum);

    save_manifest(&manifest_path, manifest).wrap_err("saving updated manifest")?;

    Ok(())
}

fn save_manifest(path: &Utf8Path, manifest: toml_edit::Document) -> Result<(), std::io::Error> {
    fs::write(path, manifest.to_string())
}

fn load_manifest(path: &Utf8Path) -> Result<toml_edit::Document, toml_edit::TomlError> {
    // Read the contents of the Cargo.toml file
    let toml_content = fs::read_to_string(path).expect("Failed to read Cargo.toml");

    // Parse it
    toml_content.parse::<toml_edit::Document>()
}

fn compute_checksum() -> String {
    let version_tag = format!(
        "{}:{}",
        DatagenProvider::LATEST_TESTED_CLDR_TAG,
        DatagenProvider::LATEST_TESTED_ICUEXPORT_TAG
    );

    let current_checksum = xxhash_rust::xxh3::xxh3_128(version_tag.as_bytes());

    format!("{current_checksum:x}")
}