use std::fs::File;
use std::io::Read as _;

use camino::Utf8Path;
use camino::Utf8PathBuf;
use miette::Context as _;
use miette::IntoDiagnostic as _;
use miette::Result;
use std::fs;
use xxhash_rust::xxh3::Xxh3;

pub fn compute_checksum_for_files(paths: &[&Utf8Path]) -> Result<String> {
    let mut hasher = Xxh3::new();

    let mut buffer = [0; 8192];
    for path in paths {
        if let Ok(mut file) = File::open(path) {
            loop {
                let bytes_read = file
                    .read(&mut buffer)
                    .into_diagnostic()
                    .wrap_err_with(|| format!("while reading file {path:?}"))?;
                if bytes_read == 0 {
                    break;
                }
                hasher.update(&buffer[..bytes_read]);
            }
        } else {
            // If file doesn't exist or cannot be opened, consider it as an empty file
            let empty_data: &[u8] = &[];
            hasher.update(empty_data);
        }
    }

    let result = hasher.digest128();
    Ok(format!("{:x}", result))
}

pub fn install_tracing() {
    use tracing_error::ErrorLayer;
    use tracing_subscriber::prelude::*;
    use tracing_subscriber::{fmt, EnvFilter};

    let fmt_layer = fmt::layer()
        .pretty()
        .with_target(false)
        .with_writer(std::io::stderr);
    let filter_layer = EnvFilter::try_from_default_env()
        .or_else(|_| EnvFilter::try_new("info"))
        .unwrap();

    tracing_subscriber::registry()
        .with(filter_layer)
        .with(fmt_layer)
        .with(ErrorLayer::default())
        .init();
}

pub fn load_manifest() -> Result<(Utf8PathBuf, toml_edit::Document)> {
    // Identify path to manifest from cargo-px
    let manifest_path =
        Utf8PathBuf::try_from(cargo_px_env::generated_pkg_manifest_path().into_diagnostic()?)
            .into_diagnostic()?;

    // Read the contents of the Cargo.toml file
    let toml_content = fs::read_to_string(&manifest_path).into_diagnostic()?;

    // Parse it
    toml_content
        .parse::<toml_edit::Document>()
        .map(|manifest| (manifest_path, manifest))
        .into_diagnostic()
}

pub fn save_manifest(manifest: toml_edit::Document) -> Result<()> {
    // Identify path to manifest from cargo-px
    let path =
        Utf8PathBuf::try_from(cargo_px_env::generated_pkg_manifest_path().into_diagnostic()?)
            .into_diagnostic()?;

    fs::write(path, manifest.to_string()).into_diagnostic()
}