use crate::{
    CurrencyCode, MerchantReference, Money, PaymentError, PaymentEvent, PaymentProvider,
    ProviderReference,
};

use super::{
    mapper::{map_event_type, map_payment_type, map_status},
    models::LipilaCallbackPayload,
};

pub(super) fn parse_callback(payload: &[u8]) -> Result<PaymentEvent, PaymentError> {
    let callback: LipilaCallbackPayload = serde_json::from_slice(payload)?;
    let status = map_status(&callback.status);
    let _operator = map_payment_type(&callback.payment_type);
    let _redacted_account_number = callback.account_number.as_str();
    let _transaction_type = callback.transaction_type.as_str();
    let _ip_address = callback.ip_address.as_deref();
    let reference = callback
        .external_id
        .as_deref()
        .or(callback.identifier.as_deref())
        .unwrap_or(callback.reference_id.as_str());
    let amount = callback
        .amount
        .as_i64()
        .map(|amount| {
            let currency = CurrencyCode::new(callback.currency.as_str())?;
            let minor = currency.major_integer_to_minor_units(amount)?;
            Money::new_minor(minor, currency.as_str())
        })
        .transpose()?;

    Ok(PaymentEvent {
        id: callback
            .identifier
            .as_deref()
            .map(crate::WebhookEventId::new)
            .transpose()?,
        provider: PaymentProvider::Lipila,
        provider_reference: ProviderReference::new(reference)?,
        merchant_reference: Some(MerchantReference::new(callback.reference_id)?),
        status,
        amount,
        event_type: map_event_type(status),
        message: callback.message,
    })
}

#[cfg(test)]
mod tests {
    use crate::{PaymentEventType, PaymentStatus};

    use super::*;

    #[test]
    fn parse_callback_normalizes_event() {
        let payload = br#"{
            "referenceId":"ORDER-1",
            "currency":"ZMW",
            "amount":50,
            "accountNumber":"260971234567",
            "status":"Successful",
            "paymentType":"MTNMoney",
            "type":"collection",
            "ipAddress":"127.0.0.1",
            "identifier":"evt_1",
            "message":"paid",
            "externalId":"LIPILA-1"
        }"#;

        let event = parse_callback(payload).expect("callback should parse");

        assert_eq!(event.provider_reference.as_str(), "LIPILA-1");
        assert_eq!(event.status, PaymentStatus::Succeeded);
        assert_eq!(event.event_type, PaymentEventType::PaymentSucceeded);
        assert_eq!(
            event
                .amount
                .expect("amount should be present")
                .amount()
                .value(),
            5_000
        );
    }
}
