// Copyright (c) Subzero Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use serde::{Deserialize, Serialize};
use validator::Validate;

use super::rpc_response_context::RpcResponseContext;
use crate::validation::{validate_airdrop_amount, validate_pubkey};

/// The request message for a requestAirdrop request.
/// Requests an airdrop of kelvins to a given account.
#[derive(Debug, Serialize, Deserialize, Validate, PartialEq)]
pub struct RequestAirdropRequest {
    #[serde(default)]
    #[validate(custom(function = crate::validation::validate_protocol_version))]
    pub version: u16,

    /// Public key of the account to airdrop to, in base58 encoding
    #[validate(length(min = 1, message = "Pubkey cannot be empty"))]
    #[validate(custom(function = validate_pubkey))]
    pub pubkey: String,

    /// Amount of kelvins to airdrop
    #[validate(custom(function = validate_airdrop_amount))]
    pub kelvins: u64,
}

impl RequestAirdropRequest {
    pub fn new(pubkey: String, kelvins: u64) -> Self {
        Self {
            version: 0,
            pubkey,
            kelvins,
        }
    }
}

/// The response message for a requestAirdrop request.
/// Returns the transaction signature of the airdrop transaction.
#[derive(Debug, Serialize, Deserialize)]
pub struct RequestAirdropResponse {
    #[serde(default)]
    pub version: u16,
    pub context: RpcResponseContext,
    pub value: String,
}

impl RequestAirdropResponse {
    pub fn new(slot: u64, signature: String) -> Self {
        Self {
            version: 0,
            context: RpcResponseContext::new(slot),
            value: signature,
        }
    }
}

#[cfg(test)]
mod tests {
    use serde_json::json;

    use super::*;

    #[test]
    fn test_valid_request_deserialization() {
        let json = json!({
            "version": 0,
            "pubkey": "83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri",
            "kelvins": 1000000
        });

        let request: RequestAirdropRequest = serde_json::from_value(json).unwrap();
        assert_eq!(
            request.pubkey,
            "83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri"
        );
        assert_eq!(request.kelvins, 1000000);
    }

    #[test]
    fn test_invalid_pubkey_validation() {
        use crate::validation::validate_request;

        let request = RequestAirdropRequest::new("not-a-valid-base58-string!".to_string(), 1000000);

        let result = validate_request(request);
        assert!(result.is_err());
    }

    #[test]
    fn test_zero_kelvins_validation() {
        use crate::validation::validate_request;

        let request = RequestAirdropRequest::new(
            "83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri".to_string(),
            0,
        );

        let result = validate_request(request);
        assert!(result.is_err());
    }
}
