use super::*;

#[test]
fn diffs_outcomes_recursively() {
    let left = FinanceOutcome {
        evaluation: AnnualFinanceEvaluation::no_action(),
        post_company: CompanyFinanceState::default(),
    };
    let mut right = left.clone();
    right.post_company.current_cash = 123;
    right.evaluation.debt_restructure = DebtRestructureSummary {
        retired_principal: 10,
        issued_principal: 20,
    };

    let report = diff_finance_outcomes(&left, &right).expect("diff should succeed");
    assert!(!report.matches);
    let diff_paths: Vec<_> = report
        .differences
        .iter()
        .map(|entry| entry.path.clone())
        .collect();
    assert_diff_paths_include(&diff_paths, "$.post_company.current_cash");
    assert_diff_paths_include(
        &diff_paths,
        "$.evaluation.debt_restructure.retired_principal",
    );
}

#[test]
fn diffs_runtime_states_recursively() {
    let left = serde_json::json!({
        "format_version": 1,
        "snapshot_id": "left",
        "state": {
            "calendar": {
                "year": 1830,
                "month_slot": 0,
                "phase_slot": 0,
                "tick_slot": 1
            },
            "world_flags": {
                "sandbox": false
            },
            "companies": []
        }
    });
    let right = serde_json::json!({
        "format_version": 1,
        "snapshot_id": "right",
        "state": {
            "calendar": {
                "year": 1830,
                "month_slot": 0,
                "phase_slot": 0,
                "tick_slot": 2
            },
            "world_flags": {
                "sandbox": true
            },
            "companies": []
        }
    });
    let left_path = write_temp_json("runtime-diff-left", &left);
    let right_path = write_temp_json("runtime-diff-right", &right);

    diff_state(&left_path, &right_path).expect("runtime diff should succeed");

    let _ = fs::remove_file(left_path);
    let _ = fs::remove_file(right_path);
}

#[test]
fn diffs_runtime_states_with_event_record_additions_and_removals() {
    let left = serde_json::json!({
        "format_version": 1,
        "snapshot_id": "left-events",
        "state": {
            "calendar": {
                "year": 1830,
                "month_slot": 0,
                "phase_slot": 0,
                "tick_slot": 1
            },
            "world_flags": {
                "sandbox": false
            },
            "companies": [],
            "event_runtime_records": [
                {
                    "record_id": 1,
                    "trigger_kind": 7,
                    "active": true
                },
                {
                    "record_id": 2,
                    "trigger_kind": 7,
                    "active": false
                }
            ]
        }
    });
    let right = serde_json::json!({
        "format_version": 1,
        "snapshot_id": "right-events",
        "state": {
            "calendar": {
                "year": 1830,
                "month_slot": 0,
                "phase_slot": 0,
                "tick_slot": 1
            },
            "world_flags": {
                "sandbox": false
            },
            "companies": [],
            "event_runtime_records": [
                {
                    "record_id": 1,
                    "trigger_kind": 7,
                    "active": true
                }
            ]
        }
    });
    let left_path = write_temp_json("runtime-diff-events-left", &left);
    let right_path = write_temp_json("runtime-diff-events-right", &right);

    let left_state =
        load_normalized_runtime_state(&left_path).expect("left runtime state should load");
    let right_state =
        load_normalized_runtime_state(&right_path).expect("right runtime state should load");
    let differences = diff_json_values(&left_state, &right_state);
    let diff_paths: Vec<_> = differences.iter().map(|entry| entry.path.clone()).collect();
    assert_diff_paths_include(&diff_paths, "$.event_runtime_records[1]");

    let _ = fs::remove_file(left_path);
    let _ = fs::remove_file(right_path);
}

#[test]
fn diffs_runtime_states_with_packed_event_collection_changes() {
    let left = serde_json::json!({
        "format_version": 1,
        "snapshot_id": "left-packed-events",
        "state": {
            "calendar": {
                "year": 1830,
                "month_slot": 0,
                "phase_slot": 0,
                "tick_slot": 1
            },
            "world_flags": {},
            "companies": [],
            "packed_event_collection": {
                "source_kind": "packed-event-runtime-collection",
                "mechanism_family": "classic-save-rehydrate-v1",
                "mechanism_confidence": "grounded",
                "container_profile_family": "rt3-classic-save-container-v1",
                "packed_state_version": 1001,
                "packed_state_version_hex": "0x000003e9",
                "live_id_bound": 5,
                "live_record_count": 3,
                "live_entry_ids": [1, 3, 5],
                "decoded_record_count": 0,
                "imported_runtime_record_count": 0,
                "records": [
                    {
                        "record_index": 0,
                        "live_entry_id": 1,
                        "decode_status": "unsupported_framing",
                        "payload_family": "unsupported_framing",
                        "grouped_effect_row_counts": [0, 0, 0, 0],
                        "decoded_actions": [],
                        "executable_import_ready": false,
                        "notes": ["left fixture"]
                    },
                    {
                        "record_index": 1,
                        "live_entry_id": 3,
                        "decode_status": "unsupported_framing",
                        "payload_family": "unsupported_framing",
                        "grouped_effect_row_counts": [0, 0, 0, 0],
                        "decoded_actions": [],
                        "executable_import_ready": false,
                        "notes": ["left fixture"]
                    },
                    {
                        "record_index": 2,
                        "live_entry_id": 5,
                        "decode_status": "unsupported_framing",
                        "payload_family": "unsupported_framing",
                        "grouped_effect_row_counts": [0, 0, 0, 0],
                        "decoded_actions": [],
                        "executable_import_ready": false,
                        "notes": ["left fixture"]
                    }
                ]
            },
            "event_runtime_records": []
        }
    });
    let right = serde_json::json!({
        "format_version": 1,
        "snapshot_id": "right-packed-events",
        "state": {
            "calendar": {
                "year": 1830,
                "month_slot": 0,
                "phase_slot": 0,
                "tick_slot": 1
            },
            "world_flags": {},
            "companies": [],
            "packed_event_collection": {
                "source_kind": "packed-event-runtime-collection",
                "mechanism_family": "classic-save-rehydrate-v1",
                "mechanism_confidence": "grounded",
                "container_profile_family": "rt3-classic-save-container-v1",
                "packed_state_version": 1001,
                "packed_state_version_hex": "0x000003e9",
                "live_id_bound": 5,
                "live_record_count": 2,
                "live_entry_ids": [1, 5],
                "decoded_record_count": 0,
                "imported_runtime_record_count": 0,
                "records": [
                    {
                        "record_index": 0,
                        "live_entry_id": 1,
                        "decode_status": "unsupported_framing",
                        "payload_family": "unsupported_framing",
                        "grouped_effect_row_counts": [0, 0, 0, 0],
                        "decoded_actions": [],
                        "executable_import_ready": false,
                        "notes": ["right fixture"]
                    },
                    {
                        "record_index": 1,
                        "live_entry_id": 5,
                        "decode_status": "unsupported_framing",
                        "payload_family": "unsupported_framing",
                        "grouped_effect_row_counts": [0, 0, 0, 0],
                        "decoded_actions": [],
                        "executable_import_ready": false,
                        "notes": ["right fixture"]
                    }
                ]
            },
            "event_runtime_records": []
        }
    });
    let left_path = write_temp_json("runtime-diff-packed-events-left", &left);
    let right_path = write_temp_json("runtime-diff-packed-events-right", &right);

    let left_state =
        load_normalized_runtime_state(&left_path).expect("left runtime state should load");
    let right_state =
        load_normalized_runtime_state(&right_path).expect("right runtime state should load");
    let differences = diff_json_values(&left_state, &right_state);

    assert!(differences.iter().any(|entry| {
        entry.path == "$.packed_event_collection.live_record_count"
            || entry.path == "$.packed_event_collection.live_entry_ids[1]"
    }));

    let _ = fs::remove_file(left_path);
    let _ = fs::remove_file(right_path);
}

#[test]
fn diffs_runtime_states_with_packed_record_and_runtime_record_import_changes() {
    let left = serde_json::json!({
        "format_version": 1,
        "snapshot_id": "left-packed-import",
        "state": {
            "calendar": {
                "year": 1830,
                "month_slot": 0,
                "phase_slot": 0,
                "tick_slot": 0
            },
            "world_flags": {},
            "companies": [],
            "packed_event_collection": {
                "source_kind": "packed-event-runtime-collection",
                "mechanism_family": "classic-save-rehydrate-v1",
                "mechanism_confidence": "grounded",
                "container_profile_family": "rt3-classic-save-container-v1",
                "packed_state_version": 1001,
                "packed_state_version_hex": "0x000003e9",
                "live_id_bound": 7,
                "live_record_count": 1,
                "live_entry_ids": [7],
                "decoded_record_count": 0,
                "imported_runtime_record_count": 0,
                "records": [
                    {
                        "record_index": 0,
                        "live_entry_id": 7,
                        "decode_status": "unsupported_framing",
                        "payload_family": "unsupported_framing",
                        "grouped_effect_row_counts": [0, 0, 0, 0],
                        "decoded_actions": [],
                        "executable_import_ready": false,
                        "notes": ["left placeholder"]
                    }
                ]
            },
            "event_runtime_records": []
        }
    });
    let right = serde_json::json!({
        "format_version": 1,
        "snapshot_id": "right-packed-import",
        "state": {
            "calendar": {
                "year": 1830,
                "month_slot": 0,
                "phase_slot": 0,
                "tick_slot": 0
            },
            "world_flags": {},
            "companies": [],
            "packed_event_collection": {
                "source_kind": "packed-event-runtime-collection",
                "mechanism_family": "classic-save-rehydrate-v1",
                "mechanism_confidence": "grounded",
                "container_profile_family": "rt3-classic-save-container-v1",
                "packed_state_version": 1001,
                "packed_state_version_hex": "0x000003e9",
                "live_id_bound": 7,
                "live_record_count": 1,
                "live_entry_ids": [7],
                "decoded_record_count": 1,
                "imported_runtime_record_count": 1,
                "records": [
                    {
                        "record_index": 0,
                        "live_entry_id": 7,
                        "payload_offset": 29186,
                        "payload_len": 64,
                        "decode_status": "executable",
                        "payload_family": "synthetic_harness",
                        "trigger_kind": 7,
                        "active": true,
                        "marks_collection_dirty": false,
                        "one_shot": false,
                        "text_bands": [
                            {
                                "label": "primary_text_band",
                                "packed_len": 5,
                                "present": true,
                                "preview": "Alpha"
                            }
                        ],
                        "standalone_condition_row_count": 1,
                        "standalone_condition_rows": [],
                        "grouped_effect_row_counts": [0, 1, 0, 0],
                        "grouped_effect_rows": [],
                        "decoded_actions": [
                            {
                                "kind": "set_world_flag",
                                "key": "from_packed_root",
                                "value": true
                            }
                        ],
                        "executable_import_ready": true,
                        "notes": ["decoded test record"]
                    }
                ]
            },
            "event_runtime_records": [
                {
                    "record_id": 7,
                    "trigger_kind": 7,
                    "active": true,
                    "marks_collection_dirty": false,
                    "one_shot": false,
                    "has_fired": false,
                    "effects": [
                        {
                            "kind": "set_world_flag",
                            "key": "from_packed_root",
                            "value": true
                        }
                    ]
                }
            ]
        }
    });
    let left_path = write_temp_json("runtime-diff-packed-import-left", &left);
    let right_path = write_temp_json("runtime-diff-packed-import-right", &right);

    let left_state =
        load_normalized_runtime_state(&left_path).expect("left runtime state should load");
    let right_state =
        load_normalized_runtime_state(&right_path).expect("right runtime state should load");
    let differences = diff_json_values(&left_state, &right_state);
    let diff_paths: Vec<_> = differences.iter().map(|entry| entry.path.clone()).collect();

    assert!(differences.iter().any(|entry| {
        entry.path == "$.packed_event_collection.records[0].decode_status"
            || entry.path == "$.packed_event_collection.records[0].decoded_actions[0]"
    }));
    assert_diff_paths_include(&diff_paths, "$.event_runtime_records[0]");

    let _ = fs::remove_file(left_path);
    let _ = fs::remove_file(right_path);
}
