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

//! # Get Recent Validator Config Hash
//!
//! This module provides types for the `getRecentValidatorConfigHash` RPC method,
//! which returns the config hash prefix required for constructing valid transactions.
//!
//! ## Purpose
//!
//! The config hash prefix is used for **replay protection across different chains**.
//! Every transaction must include the correct config hash prefix matching the target
//! chain's validator configuration. Transactions with mismatched config hashes are
//! rejected with `InvalidConfigHashPrefix` error.
//!
//! ## When to Use
//!
//! Call this endpoint:
//! - **Once at application startup** to fetch the config hash for the target network
//! - **When receiving `InvalidConfigHashPrefix` errors** to refresh a potentially stale hash
//! - **When switching between networks** (e.g., devnet → mainnet)
//!
//! ## Caching Recommendations
//!
//! The config hash is derived from the validator genesis configuration and is stable
//! for the lifetime of the network. It only changes during governance-approved
//! validator configuration updates, which are rare events.
//!
//! **Recommended caching strategy:**
//! - Cache the config hash for the duration of your application session
//! - Refresh on `InvalidConfigHashPrefix` errors (indicates config change or network switch)
//! - No need to fetch on every transaction - this adds unnecessary RPC overhead
//!
//! ## Example Usage
//!
//! ```ignore
//! // Fetch config hash once at startup
//! let config_hash = client.get_recent_validator_config_hash().await?;
//!
//! // Use it for all transactions
//! let tx = TransactionBuilder::new(payer, valid_from, config_hash.config_hash_prefix)
//!     .add_instruction(instruction)
//!     .sign(&keypair)?;
//!
//! // On InvalidConfigHashPrefix error, refresh the hash
//! match client.send_transaction(&tx).await {
//!     Err(RpcError::InvalidConfigHashPrefix) => {
//!         let new_hash = client.get_recent_validator_config_hash().await?;
//!         // Rebuild and retry transaction with new hash
//!     }
//!     result => result,
//! }
//! ```

use rialo_s_message::ConfigHashPrefix;
use serde::{Deserialize, Serialize};

/// Request for `getRecentValidatorConfigHash` RPC method.
///
/// Retrieves the config hash prefix required for constructing valid transactions.
/// This hash provides replay protection by ensuring transactions are only valid
/// on the intended chain.
///
/// # Example
///
/// ```
/// use rialo_api_types::messages::get_recent_validator_config_hash::GetRecentValidatorConfigHashRequest;
///
/// // Simple request with no parameters
/// let request = GetRecentValidatorConfigHashRequest::default();
/// ```
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct GetRecentValidatorConfigHashRequest {
    #[serde(default)]
    pub version: u16,

    /// Optional configuration parameters for the request.
    ///
    /// **Note:** This parameter is reserved for future use and is currently ignored.
    /// The endpoint always returns the current validator config hash regardless of
    /// this parameter's value.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub config: Option<ValidatorConfigHashConfig>,
}

/// Configuration parameters for the config hash request.
///
/// **Note:** These parameters are reserved for future use and are currently ignored.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ValidatorConfigHashConfig {
    /// Reserved for future use to specify which config hash variant to retrieve.
    ///
    /// Currently ignored - the endpoint always returns the current config hash.
    #[serde(rename = "type")]
    pub config_type: Option<String>,
}

/// Response containing the validator config hash prefix.
///
/// The config hash prefix is the first 64 bits of the full validator configuration
/// hash. This truncated form is sufficient for replay protection while keeping
/// transaction sizes compact.
///
/// # Example
///
/// ```
/// use rialo_api_types::messages::get_recent_validator_config_hash::GetRecentValidatorConfigHashResponse;
/// use rialo_s_message::ConfigHashPrefix;
///
/// let response = GetRecentValidatorConfigHashResponse {
///     version: 0,
///     config_hash_prefix: ConfigHashPrefix::new(12345678901234567890),
/// };
///
/// // Use in transaction construction
/// println!("Config hash prefix: {}", response.config_hash_prefix);
/// ```
#[derive(Serialize, Deserialize, Debug)]
pub struct GetRecentValidatorConfigHashResponse {
    #[serde(default)]
    pub version: u16,

    /// First 64 bits of the validator config hash.
    ///
    /// This value must be included in all transactions submitted to the network.
    /// Transactions with a mismatched config hash prefix will be rejected with
    /// `InvalidConfigHashPrefix` error.
    ///
    /// The config hash is derived from the validator genesis configuration and
    /// remains stable unless the validator configuration is updated through
    /// governance.
    #[serde(rename = "configHashPrefix")]
    pub config_hash_prefix: ConfigHashPrefix,
}

#[cfg(test)]
mod tests {
    use serde_json::{from_str, to_string};

    use super::*;

    #[test]
    fn test_response_serialization() {
        let response = GetRecentValidatorConfigHashResponse {
            version: 0,
            config_hash_prefix: ConfigHashPrefix::new(12345678901234567890),
        };
        let json = to_string(&response).unwrap();
        let expected = r#"{"version":0,"configHashPrefix":12345678901234567890}"#;
        assert_eq!(json, expected);

        let deserialized: GetRecentValidatorConfigHashResponse = from_str(&json).unwrap();
        assert_eq!(
            deserialized.config_hash_prefix,
            ConfigHashPrefix::new(12345678901234567890)
        );
    }

    #[test]
    fn test_request_empty_serialization() {
        let request = GetRecentValidatorConfigHashRequest::default();
        let json = to_string(&request).unwrap();
        let expected = r#"{"version":0}"#;
        assert_eq!(json, expected);
    }

    #[test]
    fn test_request_with_config_serialization() {
        let request = GetRecentValidatorConfigHashRequest {
            version: 0,
            config: Some(ValidatorConfigHashConfig {
                config_type: Some("genesis".to_string()),
            }),
        };
        let json = to_string(&request).unwrap();
        let expected = r#"{"version":0,"config":{"type":"genesis"}}"#;
        assert_eq!(json, expected);

        let deserialized: GetRecentValidatorConfigHashRequest = from_str(&json).unwrap();
        assert_eq!(
            deserialized.config.unwrap().config_type,
            Some("genesis".to_string())
        );
    }
}
