use super::super::*;
use crate::{arbitrary::InstanceParameters, Instance, NamedFunctionIDParameters};
use approx::AbsDiffEq;
use proptest::prelude::*;
use similar::{ChangeTag, TextDiff};

fn take_diff(expected: &Instance, actual: &Instance) -> String {
    let expected = format!("{expected:#?}");
    let actual = format!("{actual:#?}");
    let diff = TextDiff::from_lines(&expected, &actual);
    let mut diff_string = String::new();
    for change in diff.iter_all_changes() {
        let sign = match change.tag() {
            ChangeTag::Delete => "-",
            ChangeTag::Insert => "+",
            ChangeTag::Equal => " ",
        };
        diff_string += &format!("{sign}{change}");
    }
    diff_string
}

proptest! {
    #[test]
    fn test_write_mps(instance in Instance::arbitrary_with(<Instance as Arbitrary>::Parameters::default_qcqp())) {
        let mut buffer = Vec::new();
        prop_assert!(format::format(&instance, &mut buffer).is_ok())
    }

    // NOTE: MPS doesn't support named functions, so we limit the test
    // just to test against instances without named functions.
    #[test]
    fn test_roundtrip(instance in Instance::arbitrary_with(
        InstanceParameters {
            named_function_ids: NamedFunctionIDParameters::new(0, 0.into()).unwrap(),
            .. <Instance as Arbitrary>::Parameters::default_qcqp()
        })
    ) {
        let mut buffer = Vec::new();
        prop_assert!(format::format(&instance, &mut buffer).is_ok());
        let loaded = parse(&buffer[..]).unwrap();
        prop_assert!(
            instance.abs_diff_eq(&loaded, crate::ATol::default()),
            "Instance not matching after roundtrip:\n{}\nMPS:\n{}",
            take_diff(&instance, &loaded),
            String::from_utf8(buffer).unwrap()
        );
    }
}
