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

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

use super::rpc_response_context::RpcResponseContext;

/// The response message for a getTransaction request.
/// Solana specification: <https://solana.com/docs/rpc/http/gettransaction>
#[derive(Serialize, Deserialize, Debug)]
pub struct GetTransactionResponse {
    pub context: RpcResponseContext,

    /// The slot this transaction was processed in.
    pub block_height: u64,

    /// Estimated production time, as Unix timestamp (seconds since the Unix epoch) of when the
    /// transaction was processed. None if not available.
    pub block_time: Option<i64>,

    /// The transaction data object.
    pub transaction: Transaction,

    /// The transaction status metadata object.
    pub meta: TransactionStatusMetadata,
}

/// The transaction object data.
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Transaction {
    /// A list of signatures applied to the transaction in base58.
    pub signatures: Vec<String>,
    /// The content of the transaction.
    pub message: TransactionMessage,
    /// Transaction valid from (milliseconds since Unix epoch).
    pub valid_from: i64,
}

/// The transaction message object.
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct TransactionMessage {
    /// List of public keys used by the transaction.
    /// The first `message.header.num_required_signatures` public keys must sign the transaction.
    pub account_keys: Vec<String>,
    /// Details the account types and signatures required by the transaction.
    pub header: MessageHeader,
    ///  List of program instructions that will be executed in the transaction.
    pub instructions: Vec<Instruction>,
}

/// The transaction header object.
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct MessageHeader {
    /// The total number of signatures required to make the transaction valid.
    pub num_required_signatures: u8,
    /// The last `num_readonly_signed_accounts` of the signed keys are read-only accounts.
    pub num_readonly_signed_accounts: u8,
    ///  The last `num_readonly_unsigned_accounts` of the unsigned keys are read-only accounts.
    pub num_readonly_unsigned_accounts: u8,
}

/// The transaction instruction object.
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Instruction {
    /// List of ordered indices into the message.account_keys array indicating which accounts to
    /// pass to the program.
    pub accounts: Vec<u8>,
    /// The program input data encoded in a base-58 string.
    pub data: String,
    /// Index into the message.account_keys array indicating the program account that executes this
    /// instruction.
    pub program_id_index: u8,
}

/// The transaction status metadata.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TransactionStatusMetadata {
    /// Error if transaction failed, None if transaction succeeded.
    pub err: Option<serde_json::Value>,
    /// Fee this transaction was charged, as u64 integer.
    pub fee: u64,
    /// Array of string log messages or None if log message recording was not enabled during
    /// this transaction
    pub log_messages: Option<Vec<String>>,
}

/// Request for getTransaction RPC call
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Validate)]
pub struct GetTransactionRequest {
    /// Transaction signature (base58-encoded)
    #[validate(length(min = 1, message = "Signature cannot be empty"))]
    #[validate(custom(function = crate::validation::validate_signature))]
    pub signature: String,
}

impl GetTransactionRequest {
    pub fn new(signature: String) -> Self {
        Self { signature }
    }
}
