//! Deserialize-side mirror of fin-proxy's response envelope.
//!
//! `crabkey_core::ApiResponse` derives only `Serialize` (it is a server type),
//! so the SDK defines its own `Deserialize` envelope.

use serde::Deserialize;

/// The `{ success, data, error }` envelope wrapping every successful body.
#[derive(Debug, Deserialize)]
pub struct Envelope<T> {
    pub success: bool,
    // Custom `default_none` (not `#[serde(default)]`) is load-bearing: a bare
    // `#[serde(default)]` on a generic `Option<T>` field makes serde's derive
    // require `T: Default`, which payload types like `FinanceResult<…>` don't
    // implement. `default_none::<T>()` returns `None` with no bound on `T`.
    #[serde(default = "default_none")]
    pub data: Option<T>,
    #[serde(default)]
    pub error: Option<String>,
}

/// Minimal shape used to extract an error message from a non-2xx body without
/// needing the success payload type `T`.
#[derive(Debug, Deserialize)]
pub struct ErrBody {
    #[serde(default)]
    pub error: Option<String>,
}

fn default_none<T>() -> Option<T> {
    None
}

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

    #[test]
    fn parses_success_envelope() {
        let json = r#"{"success":true,"data":[1,2,3],"meta":{"request_id":"x","timestamp":"t"}}"#;
        let env: Envelope<Vec<i32>> = serde_json::from_str(json).unwrap();
        assert!(env.success);
        assert_eq!(env.data, Some(vec![1, 2, 3]));
        assert!(env.error.is_none());
    }

    #[test]
    fn parses_error_envelope() {
        let json = r#"{"success":false,"error":"bad request: nope","meta":{}}"#;
        let env: Envelope<Vec<i32>> = serde_json::from_str(json).unwrap();
        assert!(!env.success);
        assert!(env.data.is_none());
        assert_eq!(env.error.as_deref(), Some("bad request: nope"));
    }

    #[test]
    fn err_body_extracts_message() {
        let json = r#"{"success":false,"error":"unauthorized: missing key","meta":{}}"#;
        let e: ErrBody = serde_json::from_str(json).unwrap();
        assert_eq!(e.error.as_deref(), Some("unauthorized: missing key"));
    }
}
