use mig_bo4e::engine::MappingEngine;
use mig_bo4e::path_resolver::PathResolver;
use mig_bo4e::pid_schema_index::PidSchemaIndex;
use std::path::Path;

const MESSAGE_DIR: &str = "../../mappings/FV2504/UTILMD_Strom/message";
const COMMON_DIR: &str = "../../mappings/FV2504/UTILMD_Strom/common";
const SCHEMA_DIR: &str = "../../crates/mig-types/src/generated/fv2504/utilmd/pids";

fn path_resolver() -> PathResolver {
    PathResolver::from_schema_dir(Path::new(SCHEMA_DIR))
}

#[test]
fn test_load_message_level_mapping_files() {
    let msg_dir = Path::new(MESSAGE_DIR);
    if !msg_dir.exists() {
        eprintln!("message/ mappings dir not found, skipping");
        return;
    }

    let engine = MappingEngine::load(msg_dir)
        .unwrap()
        .with_path_resolver(path_resolver());
    assert_eq!(
        engine.definitions().len(),
        4,
        "Message directory should have exactly 4 mapping files (marktteilnehmer, nachricht, sg1_nachricht, kontakt)"
    );
    assert!(engine.definition_for_entity("Marktteilnehmer").is_some());
    assert!(engine.definition_for_entity("Nachricht").is_some());
    assert!(engine.definition_for_entity("Kontakt").is_some());
}

#[test]
fn test_load_real_mapping_files() {
    let msg_dir = Path::new(MESSAGE_DIR);
    let tx_dir = Path::new("../../mappings/FV2504/UTILMD_Strom/pid_55001");
    if !msg_dir.exists() || !tx_dir.exists() {
        eprintln!("mappings/ dir not found, skipping");
        return;
    }

    // Transaction-level only
    let tx_engine = MappingEngine::load(tx_dir)
        .unwrap()
        .with_path_resolver(path_resolver());
    assert!(
        tx_engine.definitions().len() >= 10,
        "Expected at least 10 transaction mapping files for pid_55001 (reduced by SG8+SG10 consolidation), got {}",
        tx_engine.definitions().len()
    );

    // Transaction engine should NOT have message-level entities
    assert!(
        tx_engine.definition_for_entity("Marktteilnehmer").is_none(),
        "Transaction engine should not have Marktteilnehmer"
    );
    assert!(
        tx_engine.definition_for_entity("Nachricht").is_none(),
        "Transaction engine should not have Nachricht"
    );

    // Transaction engine should have transaction-level entities
    assert!(tx_engine.definition_for_entity("Marktlokation").is_some());
    assert!(tx_engine
        .definition_for_entity("Geschaeftspartner")
        .is_some());
    assert!(tx_engine.definition_for_entity("Ansprechpartner").is_some());
    assert!(tx_engine
        .definition_for_entity("ProduktpaketDaten")
        .is_some());
    assert!(tx_engine.definition_for_entity("EnfgDaten").is_some());

    // Combined engine should have all entities
    let combined = MappingEngine::load_merged(&[msg_dir, tx_dir])
        .unwrap()
        .with_path_resolver(path_resolver());
    assert!(
        combined.definitions().len() >= 14,
        "Combined should have at least 14 mapping files (reduced by SG8+SG10 consolidation), got {}",
        combined.definitions().len()
    );
    assert!(combined.definition_for_entity("Marktteilnehmer").is_some());
    assert!(combined.definition_for_entity("Nachricht").is_some());
    assert!(combined.definition_for_entity("Kontakt").is_some());
    assert!(combined.definition_for_entity("Marktlokation").is_some());
    assert!(combined
        .definition_for_entity("Geschaeftspartner")
        .is_some());

    // SG10 definitions: per-CCI discriminated zuordnung files
    assert!(
        combined
            .definitions()
            .iter()
            .any(|d| d.meta.source_group == "SG4.SG8.SG10"),
        "Should have SG10 definitions"
    );
}

#[test]
fn test_marktlokation_mapping_structure() {
    let mappings_dir = Path::new("../../mappings/FV2504/UTILMD_Strom/pid_55001");
    if !mappings_dir.exists() {
        return;
    }

    let engine = MappingEngine::load(mappings_dir)
        .unwrap()
        .with_path_resolver(path_resolver());
    let def = engine
        .definitions()
        .iter()
        .find(|d| d.meta.entity == "Marktlokation" && d.meta.source_group == "SG4.SG5")
        .expect("Marktlokation SG5 definition");

    assert_eq!(def.meta.bo4e_type, "Marktlokation");
    assert_eq!(def.meta.source_group, "SG4.SG5");
    assert!(def.fields.contains_key("loc.1.0"));
    assert!(def.fields.contains_key("loc.0"));
}

#[test]
fn test_geschaeftspartner_mapping_fields() {
    let mappings_dir = Path::new("../../mappings/FV2504/UTILMD_Strom/pid_55001");
    if !mappings_dir.exists() {
        return;
    }

    let engine = MappingEngine::load(mappings_dir)
        .unwrap()
        .with_path_resolver(path_resolver());
    let def = engine.definition_for_entity("Geschaeftspartner").unwrap();

    assert_eq!(def.meta.source_group, "SG4.SG12");
    assert!(def.fields.contains_key("nad.3.0")); // name1
                                                 // Address fields are in [fields] with nested adresse.* targets
    assert!(def.fields.contains_key("nad.5")); // adresse.ort
    assert!(def.fields.contains_key("nad.7")); // adresse.postleitzahl
    assert!(def.fields.contains_key("nad.8")); // adresse.landescode
}

#[test]
fn test_load_pid_55002_mapping_files() {
    let msg_dir = Path::new(MESSAGE_DIR);
    let common_dir = Path::new(COMMON_DIR);
    let tx_dir = Path::new("../../mappings/FV2504/UTILMD_Strom/pid_55002");
    if !msg_dir.exists() || !tx_dir.exists() || !common_dir.exists() {
        eprintln!("55002 mappings/ dir not found, skipping");
        return;
    }

    // Transaction-level with common inheritance
    let schema_path = Path::new(SCHEMA_DIR).join("pid_55002_schema.json");
    let schema_index = PidSchemaIndex::from_schema_file(&schema_path).unwrap();
    let tx_engine = MappingEngine::load_with_common(common_dir, tx_dir, &schema_index)
        .unwrap()
        .with_path_resolver(path_resolver());
    assert!(
        tx_engine.definitions().len() >= 13,
        "Expected at least 13 transaction mapping files for pid_55002 (reduced by SG10 consolidation), got {}",
        tx_engine.definitions().len()
    );

    // LOC entities (transaction-level)
    assert!(tx_engine.definition_for_entity("Marktlokation").is_some());
    assert!(tx_engine.definition_for_entity("Messlokation").is_some());
    assert!(tx_engine.definition_for_entity("Netzlokation").is_some());
    assert!(tx_engine
        .definition_for_entity("SteuerbareRessource")
        .is_some());
    assert!(tx_engine
        .definition_for_entity("TechnischeRessource")
        .is_some());
    assert!(tx_engine.definition_for_entity("Prozessdaten").is_some());

    // Message-level entities should NOT be in transaction dir
    assert!(
        tx_engine.definition_for_entity("Marktteilnehmer").is_none(),
        "Transaction engine should not have Marktteilnehmer"
    );
    assert!(
        tx_engine.definition_for_entity("Nachricht").is_none(),
        "Transaction engine should not have Nachricht"
    );

    // Combined engine should have everything
    let combined = MappingEngine::load_merged(&[msg_dir, tx_dir])
        .unwrap()
        .with_path_resolver(path_resolver());
    assert!(combined.definition_for_entity("Marktteilnehmer").is_some());
    assert!(combined.definition_for_entity("Nachricht").is_some());
    assert!(combined.definition_for_entity("Kontakt").is_some());

    // SG8 info groups with discriminators
    let info_groups: Vec<&str> = tx_engine
        .definitions()
        .iter()
        .filter(|d| d.meta.source_group == "SG4.SG8" && d.meta.discriminator.is_some())
        .map(|d| d.meta.entity.as_str())
        .collect();
    assert!(
        info_groups.contains(&"Marktlokation"),
        "Should have SG8 info for Marktlokation"
    );
    assert!(
        info_groups.contains(&"Netzlokation"),
        "Should have SG8 info for Netzlokation"
    );
    assert!(
        info_groups.contains(&"SteuerbareRessource"),
        "Should have SG8 info for SteuerbareRessource"
    );
    assert!(
        info_groups.contains(&"Messlokation"),
        "Should have SG8 info for Messlokation"
    );

    // SG10 zuordnung: split per SG8 variant, each with parent entity name + source_path
    let sg10_defs: Vec<_> = tx_engine
        .definitions()
        .iter()
        .filter(|d| d.meta.source_group == "SG4.SG8.SG10")
        .collect();
    assert!(
        sg10_defs.len() >= 4,
        "Should have at least 4 SG10 definitions (one per SG8 variant), got {}",
        sg10_defs.len()
    );

    // Prozessdaten SG4 root should have both DTM+92 and DTM+93
    let prozess = tx_engine
        .definitions()
        .iter()
        .find(|d| d.meta.entity == "Prozessdaten" && d.meta.source_group == "SG4")
        .expect("Prozessdaten SG4 definition");
    assert!(
        prozess.fields.contains_key("dtm[92].0.1"),
        "55002 Prozessdaten should have DTM+92 (Beginn der Lieferung)"
    );
    assert!(
        prozess.fields.contains_key("dtm[93].0.1"),
        "55002 Prozessdaten should have DTM+93 (Vertragsende)"
    );

    // Prozessdaten RFF definitions (SG4.SG6 discriminated)
    let rff_groups: Vec<&str> = tx_engine
        .definitions()
        .iter()
        .filter(|d| d.meta.entity == "Prozessdaten" && d.meta.source_group == "SG4.SG6")
        .filter_map(|d| d.meta.discriminator.as_deref())
        .collect();
    assert!(
        rff_groups.len() >= 3,
        "Should have 3 RFF discriminators for Prozessdaten, got {}",
        rff_groups.len()
    );
}
