mod bindings;
mod cargo_skin;
mod cargo_types;
mod economy;
mod registry;
mod selector;

pub mod types;

pub const NAMED_CARGO_PRICE_DESCRIPTOR_ROW_COUNT: usize = 71;
pub const NAMED_CARGO_PRODUCTION_DESCRIPTOR_ROW_COUNT: usize = 50;
pub const CARGO_TYPE_MAGIC: u32 = 0x0000_03ea;

pub use cargo_skin::inspect_cargo_skin_pk4;
pub use cargo_types::inspect_cargo_types_dir;
pub use economy::{inspect_cargo_economy_sources, inspect_cargo_economy_sources_with_bindings};
pub use types::*;

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parses_plain_cargo_type_entry() {
        let mut bytes = Vec::new();
        bytes.extend_from_slice(&CARGO_TYPE_MAGIC.to_le_bytes());
        bytes.extend_from_slice(b"Alcohol\0");
        let entry =
            cargo_types::parse_cargo_type_entry("Alcohol.cty", &bytes).expect("entry should parse");
        assert_eq!(entry.header_magic, CARGO_TYPE_MAGIC);
        assert_eq!(entry.name.raw_name, "Alcohol");
        assert_eq!(entry.name.visible_name, "Alcohol");
        assert_eq!(entry.name.localized_string_id, None);
    }

    #[test]
    fn parses_localized_cargo_type_entry() {
        let mut bytes = Vec::new();
        bytes.extend_from_slice(&CARGO_TYPE_MAGIC.to_le_bytes());
        bytes.extend_from_slice(b"~4465Ceramics\0");
        let entry = cargo_types::parse_cargo_type_entry("~4465Ceramics.cty", &bytes)
            .expect("entry should parse");
        assert_eq!(entry.name.raw_name, "~4465Ceramics");
        assert_eq!(entry.name.visible_name, "Ceramics");
        assert_eq!(entry.name.localized_string_id, Some(4465));
    }

    #[test]
    fn parses_cargo_skin_descriptor_entry() {
        let entry = cargo_skin::parse_cargo_skin_descriptor_entry(
            "Alcohol.dsc",
            b"cargoSkin\r\nAlcohol\r\n",
        )
        .expect("descriptor should parse");
        assert_eq!(entry.descriptor_kind, "cargoSkin");
        assert_eq!(entry.name.visible_name, "Alcohol");
    }

    #[test]
    fn builds_cargo_source_report_union_counts() {
        let cargo_types = CargoTypeInspectionReport {
            directory_path: "CargoTypes".to_string(),
            entry_count: 2,
            unique_visible_name_count: 2,
            notes: Vec::new(),
            entries: vec![
                CargoTypeEntry {
                    file_name: "Alcohol.cty".to_string(),
                    file_size: 16,
                    file_size_hex: "0x10".to_string(),
                    header_magic: CARGO_TYPE_MAGIC,
                    header_magic_hex: format!("0x{CARGO_TYPE_MAGIC:08x}"),
                    name: types::parse_cargo_name_token("Alcohol"),
                },
                CargoTypeEntry {
                    file_name: "Coal.cty".to_string(),
                    file_size: 16,
                    file_size_hex: "0x10".to_string(),
                    header_magic: CARGO_TYPE_MAGIC,
                    header_magic_hex: format!("0x{CARGO_TYPE_MAGIC:08x}"),
                    name: types::parse_cargo_name_token("Coal"),
                },
            ],
        };
        let cargo_skins = CargoSkinInspectionReport {
            pk4_path: "Cargo106.PK4".to_string(),
            entry_count: 2,
            unique_visible_name_count: 2,
            notes: Vec::new(),
            entries: vec![
                CargoSkinDescriptorEntry {
                    pk4_entry_name: "Alcohol.dsc".to_string(),
                    payload_len: 20,
                    payload_len_hex: "0x14".to_string(),
                    descriptor_kind: "cargoSkin".to_string(),
                    name: types::parse_cargo_name_token("Alcohol"),
                },
                CargoSkinDescriptorEntry {
                    pk4_entry_name: "Beer.dsc".to_string(),
                    payload_len: 16,
                    payload_len_hex: "0x10".to_string(),
                    descriptor_kind: "cargoSkin".to_string(),
                    name: types::parse_cargo_name_token("Beer"),
                },
            ],
        };

        let report = economy::build_cargo_economy_source_report(cargo_types, cargo_skins, None);
        assert_eq!(report.shared_visible_name_count, 1);
        assert_eq!(report.visible_name_union_count, 3);
        assert_eq!(report.live_registry_count, 3);
        assert_eq!(
            report.cargo_type_only_visible_names,
            vec!["Coal".to_string()]
        );
        assert_eq!(
            report.cargo_skin_only_visible_names,
            vec!["Beer".to_string()]
        );
        assert_eq!(
            report.price_selector.selector_kind,
            "named_cargo_price_candidate_registry"
        );
        assert_eq!(report.price_selector.entries.len(), 3);
        assert!(!report.price_selector.exact_resolution);
    }
}
