use std::env::var;
use std::path::{Path, PathBuf};

use mavinspect::protocol::metadata::DialectTags;
use mavinspect::protocol::{Microservices, Protocol};
use mavspec_rust_gen::{Generator, GeneratorParams};

const DIALECT_LIST: [&str; 15] = [
    "all",
    "ardupilotmega",
    "asluav",
    "avssuas",
    "common",
    "cs_air_link",
    "cubepilot",
    "development",
    "icarous",
    "matrixpilot",
    "minimal",
    "paparazzi",
    "standard",
    "ualberta",
    "uavionix",
];

fn generate_rust_dialects() {
    let protocol = mavlink_message_definitions::protocol()
        .clone()
        .with_dialects_included(&included_dialects(), true, Some(included_tags()));
    let protocol = with_default_dialect(protocol);

    let params = GeneratorParams {
        microservices: enabled_microservices(),
        use_fingerprints: var("CARGO_FEATURE_FINGERPRINTS").is_ok(),
        serde: var("CARGO_FEATURE_SERDE").is_ok(),
        specta: var("CARGO_FEATURE_SPECTA").is_ok(),
        generate_tests: var("CARGO_FEATURE_GENERATE_TESTS").is_ok(),
        internal: true,
    };

    let generator = Generator::new(protocol, &mavlink_out_dir(), params);

    generator
        .generate()
        .expect("Failed to generate MAVLink dialects");
}

fn included_dialects() -> Vec<String> {
    let mut included_dialects: Vec<String> = Default::default();
    for dialect in DIALECT_LIST {
        if var(format!(
            "CARGO_FEATURE_DLCT_{}",
            dialect.to_ascii_uppercase()
        ))
        .is_ok()
        {
            included_dialects.push(dialect.to_string())
        }
    }
    if var("CARGO_CFG_DOCTEST").is_ok() {
        included_dialects.push("minimal".to_string())
    }

    included_dialects
}

fn included_tags() -> DialectTags {
    let mut included_tags: Vec<String> = Default::default();

    if var("CARGO_FEATURE_EXTRA_DIALECTS").is_ok() {
        included_tags.push("extra".to_string());
    }

    if var("CARGO_FEATURE_TEST_DIALECTS").is_ok() {
        included_tags.push("test".to_string());
    }

    DialectTags::new(&included_tags)
}

fn with_default_dialect(protocol: Protocol) -> Protocol {
    for name in mavlink_message_definitions::DEFAULT_DIALECT_SEQUENCE {
        if protocol.contains_dialect_with_canonical_name(name) {
            return protocol.with_default_dialect(name);
        }
    }
    protocol
}

fn enabled_microservices() -> Microservices {
    if var("CARGO_FEATURE_MSRV").is_err() {
        return Microservices::empty();
    }
    let mut microservices = Microservices::empty();

    if var("CARGO_FEATURE_MSRV").is_err() {
        return microservices;
    }

    for (msrv_name, msrv) in Microservices::flags_map() {
        if var(format!("CARGO_FEATURE_MSRV_{}", msrv_name)).is_ok() {
            microservices |= msrv;
        }
    }

    microservices
}

fn out_dir() -> PathBuf {
    Path::new(&var("OUT_DIR").unwrap()).to_path_buf()
}

fn mavlink_out_dir() -> PathBuf {
    out_dir().join("mavlink")
}

fn main() {
    generate_rust_dialects();
}
