use paft::money::{
    Currency, CurrencyMetadata, IsoCurrency, Locale, MAX_DECIMAL_PRECISION,
    MAX_MINOR_UNIT_DECIMALS, MoneyParseError, PriceAmount, QuantityAmount, clear_currency_metadata,
    currency_metadata, override_currency_metadata, set_currency_metadata,
};
use paft::prelude::{
    CurrencyMetadata as PreludeCurrencyMetadata, IsoCurrency as PreludeIsoCurrency,
    Locale as PreludeLocale, MAX_DECIMAL_PRECISION as PRELUDE_MAX_DECIMAL_PRECISION,
    MAX_MINOR_UNIT_DECIMALS as PRELUDE_MAX_MINOR_UNIT_DECIMALS, PriceAmount as PreludePriceAmount,
    QuantityAmount as PreludeQuantityAmount,
    override_currency_metadata as prelude_override_currency_metadata,
    set_currency_metadata as prelude_set_currency_metadata,
};

#[test]
fn facade_reexports_metadata_types_without_formatting() {
    let code = "facade_metadata";
    clear_currency_metadata(code);

    let previous: Option<CurrencyMetadata> =
        set_currency_metadata(code, "Facade Token", 4, "FT", true, Locale::EnUs)
            .expect("metadata registration should succeed");
    assert!(previous.is_none());

    let metadata: CurrencyMetadata = currency_metadata(code).expect("metadata should be present");
    assert_eq!(metadata.minor_units, 4);
    assert_eq!(metadata.default_locale, Locale::EnUs);

    let err =
        prelude_set_currency_metadata(code, "Facade Token", 5, "FT", true, PreludeLocale::EnUs)
            .expect_err("scale update through set should fail");
    assert!(matches!(
        err,
        paft::money::MinorUnitError::MinorUnitsAlreadyRegistered {
            existing: 4,
            requested: 5,
            ..
        }
    ));

    let previous: Option<PreludeCurrencyMetadata> = prelude_override_currency_metadata(
        code,
        "Facade Token",
        5,
        "FT",
        true,
        PreludeLocale::EnUs,
    )
    .expect("explicit metadata override should succeed");
    assert_eq!(
        previous
            .expect("previous metadata should be returned")
            .minor_units,
        4
    );
    assert_eq!(currency_metadata(code).unwrap().minor_units, 5);

    let previous = override_currency_metadata(code, "Facade Token", 4, "FT", true, Locale::EnUs)
        .expect("namespaced explicit metadata override should succeed");
    assert_eq!(previous.unwrap().minor_units, 5);

    clear_currency_metadata(code);
}

#[test]
fn facade_reexports_money_precision_limits() {
    assert_eq!(MAX_DECIMAL_PRECISION, PRELUDE_MAX_DECIMAL_PRECISION);
    assert_eq!(MAX_MINOR_UNIT_DECIMALS, PRELUDE_MAX_MINOR_UNIT_DECIMALS);
}

#[test]
fn facade_prelude_reexports_iso_currency() {
    let currency = Currency::Iso(PreludeIsoCurrency::USD);

    assert_eq!(currency, Currency::Iso(IsoCurrency::USD));
}

#[test]
fn facade_reexports_price_amount() {
    let amount: PriceAmount = PreludePriceAmount::new(paft::Decimal::from(123));
    assert_eq!(amount.as_decimal(), &paft::Decimal::from(123));
}

#[test]
fn facade_reexports_quantity_amount() {
    let amount: QuantityAmount =
        PreludeQuantityAmount::from_decimal(paft::Decimal::from(123)).unwrap();
    assert_eq!(amount.as_decimal(), &paft::Decimal::from(123));
}

#[test]
fn facade_reexports_currency_parse_error() {
    let err: MoneyParseError =
        Currency::try_from_str("").expect_err("empty currency should fail to parse");

    assert!(matches!(
        err,
        MoneyParseError::InvalidEnumValue {
            enum_name: "Currency",
            ..
        }
    ));
}

#[test]
fn facade_error_converts_minor_unit_errors() {
    fn configure_invalid_metadata() -> paft::Result<()> {
        set_currency_metadata("", "Invalid Token", 4, "IT", true, Locale::EnUs)?;
        Ok(())
    }

    let err = configure_invalid_metadata().expect_err("invalid metadata should fail");

    assert!(matches!(
        err,
        paft::Error::MinorUnit(paft::money::MinorUnitError::InvalidCurrencyCode { .. })
    ));
}
