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

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

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

/// Request parameters for the `getSignaturesForAddress` RPC method
#[derive(Debug, Deserialize, Serialize, Clone, Validate)]
pub struct GetSignaturesForAddressRequest {
    /// The account address to query (base58-encoded)
    #[validate(length(min = 1, message = "Address cannot be empty"))]
    #[validate(custom(function = validate_pubkey))]
    pub address: String,

    /// Optional configuration for the request
    #[validate(nested)]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub config: Option<GetSignaturesForAddressConfig>,
}

/// Configuration options for GetSignaturesForAddress request
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Validate)]
#[validate(schema(function = validate_signatures_config))]
pub struct GetSignaturesForAddressConfig {
    /// Maximum number of signatures to return (1-1000, default: 20)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub limit: Option<u16>,

    /// Start searching backwards from this transaction signature.
    /// If not provided the search starts from the top of the highest max finalized block.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub before: Option<String>,

    /// Search until this transaction signature, if found before limit reached
    #[serde(skip_serializing_if = "Option::is_none")]
    pub until: Option<String>,
}

/// Custom validation function for GetSignaturesForAddressConfig
fn validate_signatures_config(
    config: &GetSignaturesForAddressConfig,
) -> Result<(), validator::ValidationError> {
    // Validate limit if present
    if let Some(limit) = config.limit {
        validate_signature_limit(&limit)?;
    }

    // Validate before signature if present
    if let Some(ref before) = config.before {
        crate::validation::validate_signature(before)?;
    }

    // Validate until signature if present
    if let Some(ref until) = config.until {
        crate::validation::validate_signature(until)?;
    }

    Ok(())
}

/// The response message for a GetSignaturesForAddress request
#[derive(Debug, Deserialize, Serialize)]
pub struct GetSignaturesForAddressResponse {
    pub context: RpcResponseContext,
    pub value: Vec<SignatureInfo>,
}

impl GetSignaturesForAddressResponse {
    pub fn new(value: Vec<SignatureInfo>, block_height: u64) -> Self {
        Self {
            context: RpcResponseContext::new(block_height),
            value,
        }
    }
}

/// Information about a single signature returned by getSignaturesForAddress
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SignatureInfo {
    /// The transaction signature (base58-encoded)
    pub signature: String,

    /// The block in which the transaction was processed
    pub block_height: u64,

    /// Error if transaction failed, null if transaction succeeded
    #[serde(skip_serializing_if = "Option::is_none")]
    pub err: Option<TransactionError>,

    /// Block time (Unix timestamp in milliseconds) when the transaction was processed, null if not available
    #[serde(skip_serializing_if = "Option::is_none")]
    pub block_time: Option<i64>,
}

impl SignatureInfo {
    /// Create a new SignatureInfo with required fields
    pub fn new(signature: String, block_height: u64) -> Self {
        Self {
            signature,
            block_height,
            err: None,
            block_time: None,
        }
    }

    /// Create a SignatureInfo with an error
    pub fn with_error(signature: String, block_height: u64, error: TransactionError) -> Self {
        Self {
            signature,
            block_height,
            err: Some(error),
            block_time: None,
        }
    }

    /// Set the block time for this signature
    pub fn with_block_time(mut self, block_time: i64) -> Self {
        self.block_time = Some(block_time);
        self
    }
}

impl Default for GetSignaturesForAddressConfig {
    fn default() -> Self {
        Self {
            limit: Some(20),
            before: None,
            until: None,
        }
    }
}

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

    use super::*;

    #[test]
    fn test_get_signatures_for_address_request_serialization() {
        let request = GetSignaturesForAddressRequest {
            address: "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM".to_string(),
            config: Some(GetSignaturesForAddressConfig {
                limit: Some(10),
                before: Some("5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW".to_string()),
                until: None,
            }),
        };

        let json = to_string(&request).unwrap();
        let deserialized: GetSignaturesForAddressRequest = from_str(&json).unwrap();

        assert_eq!(request.address, deserialized.address);
        assert_eq!(
            request.config.as_ref().unwrap().limit,
            deserialized.config.as_ref().unwrap().limit
        );
    }

    #[test]
    fn test_signature_info_creation() {
        let signature = "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW".to_string();
        let block_height = 12345u64;

        let info = SignatureInfo::new(signature.clone(), block_height).with_block_time(1640995200);

        assert_eq!(info.signature, signature);
        assert_eq!(info.block_height, block_height);
        assert_eq!(info.block_time, Some(1640995200));
    }

    #[test]
    fn test_valid_limit_validation() {
        let config_json = json!({
            "limit": 500,
            "before": "some_signature"
        });

        let config: GetSignaturesForAddressConfig = serde_json::from_value(config_json).unwrap();
        assert_eq!(config.limit, Some(500));
    }

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

        let config = GetSignaturesForAddressConfig {
            limit: Some(1001),
            before: None,
            until: None,
        };

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

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

        let config = GetSignaturesForAddressConfig {
            limit: Some(0),
            before: None,
            until: None,
        };

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

    #[test]
    fn test_minimal_request() {
        let request = GetSignaturesForAddressRequest {
            address: "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM".to_string(),
            config: None,
        };

        let json = to_string(&request).unwrap();
        let expected = r#"{"address":"9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"}"#;
        assert_eq!(json, expected);
    }
}
