#![cfg(all(feature = "serialize", feature = "deserialize"))]
use serde::Deserialize;
use serde_saphyr::Error;

#[derive(Debug, Deserialize, PartialEq)]
struct Numbers {
    hex_i32: i32,
    oct_i32: i32,
    bin_i8: i8,
    neg_hex: i64,
    neg_bin: i16,
    u_hex: u32,
}

#[test]
fn parse_numeric_bases_default() {
    let y = r#"
hex_i32: 0x2A
oct_i32: 0o52
bin_i8: 0b1010
neg_hex: -0x2A
neg_bin: -0b11
u_hex: 0xFF
"#;
    let v: Numbers = serde_saphyr::from_str(y).expect("parse failed");
    assert_eq!(v.hex_i32, 42);
    assert_eq!(v.oct_i32, 42);
    assert_eq!(v.bin_i8, 10);
    assert_eq!(v.neg_hex, -42);
    assert_eq!(v.neg_bin, -3);
    assert_eq!(v.u_hex, 255);
}

#[derive(Debug, Deserialize, PartialEq)]
struct OnlyLegacy {
    legacy_u16: u16,
}

#[test]
fn zero_prefix_is_forbidden() {
    // legacy_octal_numbers is false by default: 052 is an error
    let y = r#"
legacy_u16: 052
"#;
    serde_saphyr::from_str::<OnlyLegacy>(y).expect_err("zero-prefixed decimals are forbidden");
}

#[test]
fn parse_numeric_bases_with_legacy_octal() {
    let y = r#"
legacy_u16: 052
"#;
    let opts = serde_saphyr::options! { legacy_octal_numbers: true };
    let v: OnlyLegacy = serde_saphyr::from_str_with_options(y, opts).expect("parse failed");
    // With legacy octal enabled, 0052 is octal -> 42 decimal
    assert_eq!(v.legacy_u16, 42);
}

#[derive(Debug, Deserialize, PartialEq)]
struct LegacyZeroMixed {
    zero_u: u16,
    plus_zero_u: u16,
    neg_zero_i: i16,
}

#[test]
fn parse_legacy_octal_zero_variants() {
    let y = r#"
zero_u: 00
plus_zero_u: +00
neg_zero_i: -00
"#;
    let opts = serde_saphyr::options! { legacy_octal_numbers: true };
    let v: LegacyZeroMixed = serde_saphyr::from_str_with_options(y, opts).expect("parse failed");
    assert_eq!(v.zero_u, 0);
    assert_eq!(v.plus_zero_u, 0);
    assert_eq!(v.neg_zero_i, 0);
}

#[test]
fn parse_legacy_octal_one() {
    let y = r#"
zero_u: 001
plus_zero_u: +001
neg_zero_i: -001
"#;
    let opts = serde_saphyr::options! { legacy_octal_numbers: true };
    let v: LegacyZeroMixed = serde_saphyr::from_str_with_options(y, opts).expect("parse failed");
    assert_eq!(v.zero_u, 1);
    assert_eq!(v.plus_zero_u, 1);
    assert_eq!(v.neg_zero_i, -1);
}

#[test]
fn parse_legacy_octal_nine() {
    let y = r#"
zero_u: 009
plus_zero_u: +009
neg_zero_i: -009
"#;
    let opts = serde_saphyr::options! { legacy_octal_numbers: true };
    let v: Result<LegacyZeroMixed, Error> = serde_saphyr::from_str_with_options(y, opts);
    assert!(v.is_err());
}

#[derive(Debug, Deserialize, PartialEq)]
struct LegacyPrefixUnderscores {
    legacy_octal: i32,
    plus_legacy_octal: i32,
    neg_legacy_octal: i32,
    hex: i32,
    octal: i32,
    binary: i32,
}

#[test]
fn parse_legacy_prefix_underscores() {
    let y = r#"
legacy_octal: 0_10
plus_legacy_octal: +0_10
neg_legacy_octal: -0_10
hex: 0x_10
octal: 0o_10
binary: 0b_10
"#;
    let opts = serde_saphyr::options! { legacy_octal_numbers: true };
    let v: LegacyPrefixUnderscores =
        serde_saphyr::from_str_with_options(y, opts).expect("parse failed");

    assert_eq!(v.legacy_octal, 0o10);
    assert_eq!(v.plus_legacy_octal, 0o10);
    assert_eq!(v.neg_legacy_octal, -0o10);
    assert_eq!(v.hex, 0x10);
    assert_eq!(v.octal, 0o10);
    assert_eq!(v.binary, 0b10);
}

#[test]
fn prefix_underscores_stay_invalid_by_default() {
    let y = r#"
legacy_octal: 0_10
plus_legacy_octal: +0_10
neg_legacy_octal: -0_10
hex: 0x_10
octal: 0o_10
binary: 0b_10
"#;

    serde_saphyr::from_str::<LegacyPrefixUnderscores>(y)
        .expect_err("prefix underscores are legacy-only");
}

#[derive(Debug, Deserialize, PartialEq)]
struct UnderscoreNumbers {
    decimal_i64: i64,
    decimal_u64: u64,
    hex_u32: u32,
    bin_u8: u8,
}

#[test]
fn parse_numeric_literals_with_underscores() {
    let y = r#"
decimal_i64: -1_234_567_890
decimal_u64: 9_876_543_210
hex_u32: 0xAB_CD_EF_01
bin_u8: 0b1010_1010
"#;
    let v: UnderscoreNumbers = serde_saphyr::from_str(y).expect("parse failed");
    assert_eq!(v.decimal_i64, -1_234_567_890);
    assert_eq!(v.decimal_u64, 9_876_543_210);
    assert_eq!(v.hex_u32, 0xAB_CD_EF_01);
    assert_eq!(v.bin_u8, 0b1010_1010);
}

#[test]
fn parse_numeric_literals_with_invalid_digits() {
    let y = r#"
hex_u32: 0xABCDG
bin_u8: 0b1021
"#;
    let err = serde_saphyr::from_str::<UnderscoreNumbers>(y).unwrap_err();
    let msg = format!("{err}");
    assert!(msg.contains("invalid u32") || msg.contains("invalid u8"));
}
