//! Tests for map_all_reverse() — reversing all definitions in an engine.

use mig_assembly::assembler::AssembledSegment;
use mig_bo4e::path_resolver::PathResolver;
use mig_bo4e::MappingEngine;
use std::path::{Path, PathBuf};

const SCHEMA_DIR: &str = "../../crates/mig-types/src/generated/fv2504/utilmd/pids";

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

fn mappings_dir() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .parent()
        .unwrap()
        .parent()
        .unwrap()
        .join("mappings/FV2504/UTILMD_Strom")
}

#[test]
fn test_map_all_reverse_message_level() {
    let msg_dir = mappings_dir().join("message");
    let msg_engine = MappingEngine::load(&msg_dir)
        .unwrap()
        .with_path_resolver(path_resolver());

    // Construct a minimal message-level BO4E JSON (Marktteilnehmer in SG2)
    let bo4e = serde_json::json!({
        "marktteilnehmer": [
            {
                "marktrolle": "MS",
                "rollencodenummer": "9900123456789"
            },
            {
                "marktrolle": "MR",
                "rollencodenummer": "9900987654321"
            }
        ]
    });

    let tree = msg_engine.map_all_reverse(&bo4e, None);

    // Should produce an AssembledTree with SG2 group containing 2 repetitions
    let sg2 = tree.groups.iter().find(|g| g.group_id == "SG2");
    assert!(sg2.is_some(), "Should have SG2 group");
    let sg2 = sg2.unwrap();
    assert_eq!(
        sg2.repetitions.len(),
        2,
        "Should have 2 Marktteilnehmer reps"
    );

    // First rep should have NAD segment with MS qualifier
    let rep0 = &sg2.repetitions[0];
    let nad = rep0.segments.iter().find(|s| s.tag == "NAD");
    assert!(nad.is_some(), "First SG2 rep should have NAD");
}

#[test]
fn test_map_all_reverse_transaction_level() {
    let tx_dir = mappings_dir().join("pid_55001");
    let tx_engine = MappingEngine::load(&tx_dir)
        .unwrap()
        .with_path_resolver(path_resolver());

    // Minimal transaction-level BO4E (Marktlokation in SG5)
    let bo4e = serde_json::json!({
        "marktlokation": {
            "marktlokationsId": "51238696781"
        }
    });

    let tree = tx_engine.map_all_reverse(&bo4e, None);

    // Should produce groups including SG5
    let has_groups = !tree.groups.is_empty();
    assert!(
        has_groups,
        "Should produce at least one group from reverse mapping"
    );
}

/// Test: flat companion fields (typed PID format) are found without *Edifact wrapper.
#[test]
fn test_map_all_reverse_flat_companion_fields() {
    let tx_dir = mappings_dir().join("pid_55001");
    let tx_engine = MappingEngine::load(&tx_dir)
        .unwrap()
        .with_path_resolver(path_resolver());

    // Typed PID format: companion fields (haushaltskunde) at entity root,
    // NOT under marktlokationEdifact wrapper.
    let bo4e_flat = serde_json::json!({
        "marktlokation": {
            "marktlokationsId": "51238696781",
            "haushaltskunde": "Z18"
        }
    });
    // Forward mapping format: haushaltskunde under *Edifact wrapper
    let bo4e_wrapped = serde_json::json!({
        "marktlokation": {
            "marktlokationsId": "51238696781",
            "marktlokationEdifact": {
                "haushaltskunde": "Z18"
            }
        }
    });

    let tree_flat = tx_engine.map_all_reverse(&bo4e_flat, None);
    let tree_wrapped = tx_engine.map_all_reverse(&bo4e_wrapped, None);

    // Both formats should produce the same groups
    assert_eq!(
        tree_flat.groups.len(),
        tree_wrapped.groups.len(),
        "Flat and wrapped companion formats should produce the same number of groups"
    );

    // Both should have an SG10 group (before nesting into SG8)
    // CCI/CAV segments for haushaltskunde originate from SG4.SG8.SG10
    let flat_has_sg10 = tree_flat.groups.iter().any(|g| g.group_id == "SG10");
    let wrapped_has_sg10 = tree_wrapped.groups.iter().any(|g| g.group_id == "SG10");
    assert!(
        wrapped_has_sg10,
        "Wrapped format should produce SG10 group with CCI/CAV for haushaltskunde"
    );
    assert_eq!(
        flat_has_sg10, wrapped_has_sg10,
        "Flat companion format should produce the same SG10 group as wrapped format"
    );
}

/// Test: forward-format BO4E with *Edifact wrapper still works (no regression).
/// Verified by the flat_companion_fields test above which compares both formats.

/// Test: map-keyed geschaeftspartner (typed PID format) is unwrapped correctly.
#[test]
fn test_map_all_reverse_map_keyed_geschaeftspartner() {
    let tx_dir = mappings_dir().join("pid_55001");
    let tx_engine = MappingEngine::load(&tx_dir)
        .unwrap()
        .with_path_resolver(path_resolver());

    // Forward format: flat geschaeftspartner with *Edifact wrapper
    let bo4e_forward = serde_json::json!({
        "geschaeftspartner": {
            "name1": "Schwarz",
            "name2": "Nicole",
            "adresse": {
                "strasse": "Königstraße",
                "hausnummer": "30",
                "ort": "Stuttgart",
                "postleitzahl": "70173",
                "landescode": "DE"
            },
            "geschaeftspartnerEdifact": {
                "nadQualifier": "Z04",
                "anredeCode": "Z01",
                "anrede": "Herr"
            }
        }
    });

    // Typed PID format: map-keyed with flat companion fields
    let bo4e_typed = serde_json::json!({
        "geschaeftspartner": {
            "Z04": {
                "name1": "Schwarz",
                "name2": "Nicole",
                "adresse": {
                    "strasse": "Königstraße",
                    "hausnummer": "30",
                    "ort": "Stuttgart",
                    "postleitzahl": "70173",
                    "landescode": "DE"
                },
                "anredeCode": "Z01",
                "anrede": "Herr"
            }
        }
    });

    let tree_forward = tx_engine.map_all_reverse(&bo4e_forward, None);
    let tree_typed = tx_engine.map_all_reverse(&bo4e_typed, None);

    // Both should produce SG12 group with NAD segment
    let sg12_forward = tree_forward.groups.iter().find(|g| g.group_id == "SG12");
    let sg12_typed = tree_typed.groups.iter().find(|g| g.group_id == "SG12");
    assert!(sg12_forward.is_some(), "Forward format should produce SG12");
    assert!(sg12_typed.is_some(), "Typed PID format should produce SG12");

    let reps_forward = &sg12_forward.unwrap().repetitions;
    let reps_typed = &sg12_typed.unwrap().repetitions;
    assert_eq!(
        reps_forward.len(),
        reps_typed.len(),
        "Both formats should produce the same number of SG12 reps"
    );

    // Both NAD segments should have the same data
    let nad_forward = reps_forward[0].segments.iter().find(|s| s.tag == "NAD").unwrap();
    let nad_typed = reps_typed[0].segments.iter().find(|s| s.tag == "NAD").unwrap();

    // Check name is present in both
    let has_name = |nad: &AssembledSegment| {
        nad.elements.iter().any(|el: &Vec<String>| el.iter().any(|c| c == "Schwarz"))
    };
    assert!(has_name(nad_forward), "Forward NAD should have name 'Schwarz'");
    assert!(has_name(nad_typed), "Typed NAD should have name 'Schwarz'");

    // Check Z04 qualifier is present in both
    let has_qualifier = |nad: &AssembledSegment| {
        nad.elements.iter().any(|el: &Vec<String>| el.iter().any(|c| c == "Z04"))
    };
    assert!(has_qualifier(nad_forward), "Forward NAD should have Z04 qualifier");
    assert!(has_qualifier(nad_typed), "Typed NAD should have Z04 qualifier (injected from map key)");
}

/// Test: map-keyed marktteilnehmer (non-discriminated) is converted to array.
#[test]
fn test_map_all_reverse_map_keyed_marktteilnehmer() {
    let msg_dir = mappings_dir().join("message");
    let msg_engine = MappingEngine::load(&msg_dir)
        .unwrap()
        .with_path_resolver(path_resolver());

    // Typed PID format: marktteilnehmer keyed by role code
    let bo4e = serde_json::json!({
        "marktteilnehmer": {
            "MS": {
                "marktrolle": "MS",
                "rollencodenummer": "9900123456789"
            },
            "MR": {
                "marktrolle": "MR",
                "rollencodenummer": "9900987654321"
            }
        }
    });

    let tree = msg_engine.map_all_reverse(&bo4e, None);

    let sg2 = tree.groups.iter().find(|g| g.group_id == "SG2");
    assert!(sg2.is_some(), "Should have SG2 group for Marktteilnehmer");
    let sg2 = sg2.unwrap();
    assert_eq!(
        sg2.repetitions.len(),
        2,
        "Map-keyed marktteilnehmer should produce 2 reps (MS + MR)"
    );
}

/// Test: array-format entities (typed PID format) work correctly.
#[test]
fn test_map_all_reverse_array_entities() {
    let tx_dir = mappings_dir().join("pid_55001");
    let tx_engine = MappingEngine::load(&tx_dir)
        .unwrap()
        .with_path_resolver(path_resolver());

    // Typed PID format: entities as arrays
    let bo4e = serde_json::json!({
        "marktlokation": [{
            "marktlokationsId": "93733631127",
            "haushaltskunde": "Z18"
        }],
        "ansprechpartner": [{
            "nachname": "Schwarz",
            "vorname": "Nicole",
            "anrede": "Herr",
            "anredeCode": "Z01"
        }]
    });

    let tree = tx_engine.map_all_reverse(&bo4e, None);

    // Marktlokation should produce SG5 with LOC segment
    let sg5 = tree.groups.iter().find(|g| g.group_id == "SG5");
    assert!(sg5.is_some(), "Should have SG5 group for Marktlokation");

    // Ansprechpartner should produce SG12 with NAD+Z09
    let sg12 = tree.groups.iter().find(|g| g.group_id == "SG12");
    assert!(sg12.is_some(), "Should have SG12 group for Ansprechpartner");
    let nad = sg12.unwrap().repetitions[0]
        .segments
        .iter()
        .find(|s| s.tag == "NAD");
    assert!(nad.is_some(), "Ansprechpartner SG12 should have NAD segment");
}
