use hmac::{Hmac, Mac};
use sha2::Sha256;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
pub struct StripeEvent {
    #[serde(rename = "type")]
    pub event_type: String,
    pub data: StripeEventData,
}

#[derive(Debug, Deserialize)]
pub struct StripeEventData {
    pub object: serde_json::Value,
}

pub fn verify_webhook_signature(payload: &str, signature: &str, secret: &str) -> bool {
    let parts: std::collections::HashMap<&str, &str> = signature
        .split(',')
        .filter_map(|part| {
            let mut kv = part.splitn(2, '=');
            Some((kv.next()?, kv.next()?))
        })
        .collect();

    let timestamp = match parts.get("t") {
        Some(t) => *t,
        None => return false,
    };

    let expected_sig = match parts.get("v1") {
        Some(s) => *s,
        None => return false,
    };

    let signed_payload = format!("{}.{}", timestamp, payload);

    let mut mac = match Hmac::<Sha256>::new_from_slice(secret.as_bytes()) {
        Ok(m) => m,
        Err(_) => return false,
    };
    mac.update(signed_payload.as_bytes());

    let expected_bytes = match hex::decode(expected_sig) {
        Ok(b) => b,
        Err(_) => return false,
    };
    mac.verify_slice(&expected_bytes).is_ok()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parse_checkout_completed_event() {
        let payload = r#"{"type":"checkout.session.completed","data":{"object":{"customer":"cus_123","metadata":{"user_id":"usr_456"}}}}"#;
        let event: StripeEvent = serde_json::from_str(payload).unwrap();
        assert_eq!(event.event_type, "checkout.session.completed");
    }

    #[test]
    fn parse_subscription_deleted_event() {
        let payload = r#"{"type":"customer.subscription.deleted","data":{"object":{"customer":"cus_789"}}}"#;
        let event: StripeEvent = serde_json::from_str(payload).unwrap();
        assert_eq!(event.event_type, "customer.subscription.deleted");
    }

    #[test]
    fn webhook_signature_rejects_bad_signature() {
        assert!(!verify_webhook_signature("payload", "t=123,v1=badsig", "secret"));
    }

    #[test]
    fn webhook_signature_accepts_valid() {
        use hmac::{Hmac, Mac};
        use sha2::Sha256;

        let secret = "whsec_test";
        let payload = "payload";
        let timestamp = "1234";
        let signed_payload = format!("{timestamp}.{payload}");

        let mut mac = Hmac::<Sha256>::new_from_slice(secret.as_bytes()).unwrap();
        mac.update(signed_payload.as_bytes());
        let sig_hex = hex::encode(mac.finalize().into_bytes());

        let header = format!("t={timestamp},v1={sig_hex}");
        assert!(verify_webhook_signature(payload, &header, secret));
    }

    #[test]
    fn webhook_signature_rejects_missing_timestamp() {
        assert!(!verify_webhook_signature("payload", "v1=abcdef", "secret"));
    }

    #[test]
    fn webhook_signature_rejects_missing_v1() {
        assert!(!verify_webhook_signature("payload", "t=123456", "secret"));
    }

    #[test]
    fn webhook_signature_rejects_invalid_hex() {
        assert!(!verify_webhook_signature("payload", "t=123,v1=zzzz", "secret"));
    }

    #[test]
    fn webhook_signature_rejects_empty() {
        assert!(!verify_webhook_signature("payload", "", "secret"));
    }
}
