use alloc::{sync::Arc, vec, vec::Vec};
use std::{fs, path::Path, println};

use miden_assembly_syntax::{
    ast::{
        Path as AstPath, PathBuf,
        types::{CallConv, FunctionType, Type},
    },
    semver::Version,
};
use miden_core::{
    mast::{BasicBlockNodeBuilder, MastForest, MastForestContributor, MastNodeExt, MastNodeId},
    operations::Operation,
    serde::Serializable,
};

use super::{PackageId, TargetType};
use crate::{Package, PackageExport, ProcedureExport};

fn build_forest() -> (MastForest, MastNodeId) {
    let mut forest = MastForest::new();
    let node_id = BasicBlockNodeBuilder::new(vec![Operation::Add])
        .add_to_forest(&mut forest)
        .expect("failed to build basic block");
    forest.make_root(node_id);
    (forest, node_id)
}

fn absolute_path(name: &str) -> Arc<AstPath> {
    let path = PathBuf::new(name).expect("invalid path");
    let path = path.as_path().to_absolute().unwrap().into_owned();
    Arc::from(path.into_boxed_path())
}

fn build_package_exports(signature: Option<FunctionType>) -> (Arc<MastForest>, Vec<PackageExport>) {
    let (forest, node_id) = build_forest();
    let root = forest[node_id].digest();
    let path = absolute_path("test::proc");
    let export = ProcedureExport::new(Arc::clone(&path), Some(node_id), root, signature);

    (Arc::new(forest), vec![PackageExport::Procedure(export)])
}

fn build_package(signature: Option<FunctionType>) -> Package {
    let (mast, exports) = build_package_exports(signature);
    Package::create(
        PackageId::from("test_pkg"),
        Version::new(0, 0, 0),
        TargetType::Library,
        mast,
        exports,
        None,
    )
    .expect("seed package should be valid")
}

#[test]
#[ignore = "run manually to generate fuzz seeds"]
fn generate_fuzz_seeds() {
    fn write_seed(target: &str, name: &str, bytes: &[u8]) {
        let corpus_root =
            Path::new(env!("CARGO_MANIFEST_DIR")).join("../../miden-core-fuzz/corpus");
        let corpus_dir = corpus_root.join(target);
        fs::create_dir_all(&corpus_dir).expect("failed to create corpus directory");
        fs::write(corpus_dir.join(name), bytes).expect("failed to write seed");
        println!("Generated {}/{} ({} bytes)", target, name, bytes.len());
    }

    let package = build_package(None);
    write_seed("package_deserialize", "minimal_package.bin", &package.to_bytes());
    write_seed("package_semantic_deserialize", "minimal_package.bin", &package.to_bytes());

    let signature = FunctionType::new(CallConv::Fast, [Type::Felt], [Type::Felt]);
    let package_with_signature = build_package(Some(signature));
    write_seed(
        "package_deserialize",
        "package_with_signature.bin",
        &package_with_signature.to_bytes(),
    );

    println!("\nSeed corpus generated in ../../miden-core-fuzz/corpus");
}
