use serde_json::Value;

pub fn compare_expected_state_fragment(expected: &Value, actual: &Value) -> Vec<String> {
    let mut mismatches = Vec::new();
    compare_expected_state_fragment_at_path("$", expected, actual, &mut mismatches);
    mismatches
}

fn compare_expected_state_fragment_at_path(
    path: &str,
    expected: &Value,
    actual: &Value,
    mismatches: &mut Vec<String>,
) {
    match (expected, actual) {
        (Value::Object(expected_map), Value::Object(actual_map)) => {
            for (key, expected_value) in expected_map {
                let next_path = format!("{path}.{key}");
                match actual_map.get(key) {
                    Some(actual_value) => compare_expected_state_fragment_at_path(
                        &next_path,
                        expected_value,
                        actual_value,
                        mismatches,
                    ),
                    None => mismatches.push(format!("{next_path} missing in actual state")),
                }
            }
        }
        (Value::Array(expected_items), Value::Array(actual_items)) => {
            for (index, expected_item) in expected_items.iter().enumerate() {
                let next_path = format!("{path}[{index}]");
                match actual_items.get(index) {
                    Some(actual_item) => compare_expected_state_fragment_at_path(
                        &next_path,
                        expected_item,
                        actual_item,
                        mismatches,
                    ),
                    None => mismatches.push(format!("{next_path} missing in actual state")),
                }
            }
        }
        _ if expected != actual => mismatches.push(format!(
            "{path} mismatch: expected {expected:?}, got {actual:?}"
        )),
        _ => {}
    }
}
