//! Conformance vectors lifted verbatim from CPython's own test suite,
//! `Lib/test/test_types.py::TypesTests.test_int__format__` (CPython 3.13), plus a few documented
//! `str` cases. Each `int` row below appears as a `test(value, spec, expected)` call in CPython.
//!
//! Static, Python-free proof (`cargo test`); the exhaustive differential lives in `difftest.py`
//! (46k+ ops vs the live `format()` builtin).

use pyformat_rs::{Value, format_float, format_int, format_str, str_format};

fn fi(v: i128, spec: &str) -> String {
    format_int(v, spec).unwrap()
}
fn fs(v: &str, spec: &str) -> String {
    format_str(v, spec).unwrap()
}
fn ff(v: f64, spec: &str) -> String {
    format_float(v, spec).unwrap()
}

// test_types.py: test_int__format__ (decimal, sign, alignment)
#[test]
fn int_decimal_and_sign() {
    assert_eq!(fi(123456789, "d"), "123456789");
    assert_eq!(fi(1, "-"), "1");
    assert_eq!(fi(-1, "-"), "-1");
    assert_eq!(fi(1, "-3"), "  1");
    assert_eq!(fi(-1, "-3"), " -1");
    assert_eq!(fi(1, "+3"), " +1");
    assert_eq!(fi(1, " 3"), "  1");
    assert_eq!(fi(-1, " "), "-1");
    assert_eq!(fi(1, " "), " 1");
}

// test_types.py: test_int__format__ (hex / octal / binary)
#[test]
fn int_bases() {
    assert_eq!(fi(1234, "x"), "4d2");
    assert_eq!(fi(-1234, "x"), "-4d2");
    assert_eq!(fi(1234, "8x"), "     4d2");
    assert_eq!(fi(-1234, "8x"), "    -4d2");
    assert_eq!(fi(0xbe, "X"), "BE");
    assert_eq!(fi(-0xbe, "X"), "-BE");
    assert_eq!(fi(65, "o"), "101");
    assert_eq!(fi(1234, "o"), "2322");
    assert_eq!(fi(1234, "+o"), "+2322");
    assert_eq!(fi(1234, "b"), "10011010010");
    assert_eq!(fi(-1234, "b"), "-10011010010");
    assert_eq!(fi(1234, " b"), " 10011010010");
}

// test_types.py: test_int__format__ (alternate '#' form)
#[test]
fn int_alternate() {
    assert_eq!(fi(0, "#b"), "0b0");
    assert_eq!(fi(-1, "-#5b"), " -0b1");
    assert_eq!(fi(100, "+#b"), "+0b1100100");
    assert_eq!(fi(100, "#012b"), "0b0001100100");
    assert_eq!(fi(-100, "#012b"), "-0b001100100");
    assert_eq!(fi(100, "#012o"), "0o0000000144");
    assert_eq!(fi(123456, "#012x"), "0x000001e240");
    assert_eq!(fi(-123456, "#012x"), "-0x00001e240");
    assert_eq!(fi(123456, "#012X"), "0X000001E240");
    assert_eq!(fi(-1, "-#5X"), " -0X1");
}

// test_types.py: test_int__format__ (thousands grouping, incl. issue 5782 zero-fill)
#[test]
fn int_grouping() {
    assert_eq!(fi(1234, ","), "1,234");
    assert_eq!(fi(-1234567, ","), "-1,234,567");
    assert_eq!(fi(123456, ","), "123,456");
    assert_eq!(fi(1234, "010,"), "00,001,234");
}

// test_types.py: test_int__format__ ('c' presentation type)
#[test]
fn int_char() {
    assert_eq!(fi(1, "c"), "\u{01}");
    assert_eq!(fi(65, "c"), "A");
}

// Documented str format-spec behaviour (Python docs / format() builtin).
#[test]
fn str_basic() {
    assert_eq!(fs("hi", "<8"), "hi      ");
    assert_eq!(fs("hi", ">8"), "      hi");
    assert_eq!(fs("hi", "^8"), "   hi   ");
    assert_eq!(fs("hello", ".3"), "hel");
    assert_eq!(fs("hello", "8.3"), "hel     ");
    assert_eq!(fs("hi", "*<8"), "hi******");
    assert_eq!(fs("hi", "08"), "hi000000");
}

// test_types.py: test_float__format__ (presentation types, sign, exponent)
#[test]
fn float_types() {
    assert_eq!(ff(0.0, "f"), "0.000000");
    assert_eq!(ff(0.0, ""), "0.0");
    assert_eq!(ff(0.01, ""), "0.01");
    assert_eq!(ff(0.01, "g"), "0.01");
    assert_eq!(ff(1.0, " g"), " 1");
    assert_eq!(ff(1.0, "+g"), "+1");
    assert_eq!(ff(1.1234e200, "g"), "1.1234e+200");
    assert_eq!(ff(1.1234e200, "G"), "1.1234E+200");
    assert_eq!(ff(1.0, "e"), "1.000000e+00");
    assert_eq!(ff(-1.0, "E"), "-1.000000E+00");
    assert_eq!(ff(1.1234e20, "e"), "1.123400e+20");
    assert_eq!(ff(1e200, "+"), "+1e+200");
    assert_eq!(ff(1.1e200, "+"), "+1.1e+200");
}

// test_types.py: test_float__format__ (zero-fill, grouping, precision)
#[test]
fn float_padding_grouping() {
    assert_eq!(ff(1234., "012f"), "01234.000000");
    assert_eq!(ff(-1234., "013f"), "-01234.000000");
    assert_eq!(ff(-1234.12341234, "013f"), "-01234.123412");
    assert_eq!(ff(-123456.12341234, "011.2f"), "-0123456.12");
    assert_eq!(ff(1.2, "010,.2"), "0,000,001.2");
    assert_eq!(ff(1234., "013,f"), "01,234.000000");
    assert_eq!(ff(-1234., "014,f"), "-01,234.000000");
}

// Documented float edges (Python docs / observed behaviour).
#[test]
fn float_edges() {
    assert_eq!(ff(2.5, ".0f"), "2"); // round half to even
    assert_eq!(ff(3.5, ".0f"), "4");
    assert_eq!(ff(0.5, "%"), "50.000000%");
    assert_eq!(ff(1e16, ""), "1e+16");
    assert_eq!(ff(1e-5, ""), "1e-05");
    assert_eq!(ff(0.0001, ""), "0.0001");
    assert_eq!(ff(-0.0, ".2f"), "-0.00");
    assert_eq!(ff(-0.001, "z.2f"), "0.00"); // z coerces negative zero
    assert_eq!(ff(42.0, "#.0f"), "42.");
    assert_eq!(ff(f64::INFINITY, "f"), "inf");
    assert_eq!(ff(f64::NEG_INFINITY, ""), "-inf");
    assert_eq!(ff(f64::NAN, "F"), "NAN");
    assert_eq!(format_int(1234567, ".2g").unwrap(), "1.2e+06"); // int promotes to float
}

// str.format mechanics (Python docs / observed CPython 3.13 behaviour).
#[test]
fn str_format_mechanics() {
    let i = |n| Value::Int(n);
    let s = |t: &str| Value::Str(t.to_string());

    // auto vs manual numbering, reuse
    assert_eq!(str_format("{} {} {}", &[i(1), i(2), i(3)], &[]).unwrap(), "1 2 3");
    assert_eq!(str_format("{0} {1} {0}", &[s("a"), s("b")], &[]).unwrap(), "a b a");
    // keyword
    assert_eq!(str_format("{name}", &[], &[("name".into(), s("x"))]).unwrap(), "x");
    // brace escapes
    assert_eq!(str_format("{{{0}}}", &[i(5)], &[]).unwrap(), "{5}");
    // conversions
    assert_eq!(str_format("{!r}", &[s("hi")], &[]).unwrap(), "'hi'");
    assert_eq!(str_format("{!a}", &[s("\u{e9}\u{f1}")], &[]).unwrap(), "'\\xe9\\xf1'");
    assert_eq!(str_format("{!r:>10}", &[s("hi")], &[]).unwrap(), "      'hi'");
    // nested replacement fields in the spec
    assert_eq!(str_format("{:{}.{}}", &[Value::Float(3.14159), i(10), i(2)], &[]).unwrap(), "       3.1");
    assert_eq!(str_format("{0:{1}}", &[i(42), s("#x")], &[]).unwrap(), "0x2a");
    // bool / None __format__
    assert_eq!(str_format("{:d}", &[Value::Bool(true)], &[]).unwrap(), "1");
    assert_eq!(str_format("{}", &[Value::Bool(true)], &[]).unwrap(), "True");
    assert_eq!(str_format("{}", &[Value::None], &[]).unwrap(), "None");
}

// test_str.py: test_format (positional, escapes, str spec edges, fill chars, huge width)
#[test]
fn str_format_cpython_rows() {
    let s = |t: &str| Value::Str(t.to_string());
    let sf = |t: &str, a: &[Value]| str_format(t, a, &[]).unwrap();

    assert_eq!(sf("", &[]), "");
    assert_eq!(sf("{0}", &[s("abc")]), "abc");
    assert_eq!(sf("{0:}", &[s("abc")]), "abc");
    assert_eq!(sf("X{0}Y", &[s("abc")]), "XabcY");
    assert_eq!(sf("{1}", &[Value::Int(1), s("abc")]), "abc");
    assert_eq!(sf("{0}", &[Value::Int(-15)]), "-15");
    assert_eq!(sf("{0}{1}", &[Value::Int(-15), s("abc")]), "-15abc");
    assert_eq!(sf("{{", &[]), "{");
    assert_eq!(sf("}}", &[]), "}");
    assert_eq!(sf("{{}}", &[]), "{}");
    assert_eq!(sf("{{{0}}}", &[Value::Int(123)]), "{123}");
    assert_eq!(sf("{{{{0}}}}", &[]), "{{0}}");
    assert_eq!(sf("}}{{", &[]), "}{");

    // string spec edges
    assert_eq!(sf("{0:.3s}", &[s("abcdef")]), "abc");
    assert_eq!(sf("{0:.0s}", &[s("abcdef")]), "");
    assert_eq!(sf("{0:3.2s}", &[s("abc")]), "ab ");
    assert_eq!(sf("{0:x<7s}", &[s("result")]), "resultx");
    assert_eq!(sf("{0:>8s}", &[s("result")]), "  result");
    assert_eq!(sf("{0:^9s}", &[s("result")]), " result  ");
    assert_eq!(sf("{0:08s}", &[s("result")]), "result00");
    assert_eq!(sf("{0:>08s}", &[s("result")]), "00result");
    assert_eq!(sf("{0:^08s}", &[s("result")]), "0result0");

    // huge width (exercises the unbounded zero/space fill, not Rust's capped format!)
    assert_eq!(sf("{0:10000}", &[s("a")]), format!("a{}", " ".repeat(9999)));

    // null-byte and control-char fill (issue 12546)
    assert_eq!(sf("{0:\u{0}<6s}", &[s("foo")]), "foo\u{0}\u{0}\u{0}");
    assert_eq!(sf("{0:\u{0}^6}", &[Value::Int(3)]), "\u{0}\u{0}3\u{0}\u{0}\u{0}");
    assert_eq!(sf("{0:\u{0}<6}", &[Value::Float(3.14)]), "3.14\u{0}\u{0}");
}

// str.format errors raised by CPython.
#[test]
fn str_format_errors() {
    assert!(str_format("{0} {}", &[Value::Int(1), Value::Int(2)], &[]).is_err()); // mix manual+auto
    assert!(str_format("{} {0}", &[Value::Int(1), Value::Int(2)], &[]).is_err()); // mix auto+manual
    assert!(str_format("{:>5}", &[Value::None], &[]).is_err()); // None with non-empty spec
    assert!(str_format("{!x}", &[Value::Int(1)], &[]).is_err()); // unknown conversion
    assert!(str_format("{", &[], &[]).is_err()); // single '{'
    assert!(str_format("}", &[], &[]).is_err()); // single '}'
    assert!(str_format("{3}", &[Value::Int(1)], &[]).is_err()); // index out of range
    assert!(str_format("{missing}", &[], &[]).is_err()); // unknown keyword
}

// Spec errors raised by CPython (ValueError).
#[test]
fn errors() {
    assert!(format_str("hi", "d").is_err()); // type 'd' invalid for str
    assert!(format_str("hi", "+").is_err()); // sign invalid for str
    assert!(format_str("hi", "=8").is_err()); // explicit '=' invalid for str
    assert!(format_int(42, ".2").is_err()); // precision invalid for int
    assert!(format_int(42, ",x").is_err()); // ',' invalid with 'x'
    assert!(format_int(42, ",_d").is_err()); // both ',' and '_'
    assert!(format_int(65, "+c").is_err()); // sign invalid with 'c'
}
