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

use rialo_s_pubkey::Pubkey;
#[cfg(feature = "non-pdk")]
use rialo_s_sdk::inner_instruction::InnerInstructionsList;
use serde::{Deserialize, Serialize};

/// Details about the fees associated with a transaction.
///
/// This structure contains information about both the base transaction fee
/// and any additional prioritization fees that may have been applied.
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
pub struct FeeDetails {
    /// The base transaction fee required for processing the transaction
    pub transaction_fee: u64,
    /// Additional fee paid to prioritize the transaction
    pub prioritization_fee: u64,
}

impl FeeDetails {
    /// Creates a new `FeeDetails` instance with the specified fees.
    ///
    /// # Arguments
    ///
    /// * `transaction_fee` - The base transaction fee
    /// * `prioritization_fee` - The additional prioritization fee
    ///
    /// # Returns
    ///
    /// A new `FeeDetails` instance with the specified fees
    pub fn new(transaction_fee: u64, prioritization_fee: u64) -> Self {
        Self {
            transaction_fee,
            prioritization_fee,
        }
    }

    /// Calculates the total fee by adding the transaction fee and prioritization fee.
    ///
    /// Uses saturating addition to prevent overflow.
    ///
    /// # Returns
    ///
    /// The sum of the transaction fee and prioritization fee
    pub fn total_fee(&self) -> u64 {
        self.transaction_fee.saturating_add(self.prioritization_fee)
    }
}

/// Re-export InstructionError from rialo-shared-types for backwards compatibility
pub use rialo_shared_types::InstructionError;
/// Re-export TransactionError from rialo-shared-types for backwards compatibility
pub use rialo_shared_types::TransactionError;

/// Execution results of a transaction.
///
/// This structure contains the complete execution results for a transaction,
/// including timing information, block height, and detailed execution results.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TransactionExecutionResults {
    /// Unix timestamp when the transaction was executed
    pub timestamp: u64,
    /// Block height at which the transaction was included
    pub block_height: u64,
    /// Detailed execution results of the committed transaction
    pub committed_transaction: CommittedTransaction,
    /// The 0-based index of this transaction within its block.
    #[serde(default)]
    pub transaction_index_in_block: u32,
    /// The pubkey of the subscription that triggered this transaction.
    /// None if the transaction was not triggered by a subscription.
    #[serde(default)]
    pub subscription_pubkey: Option<Pubkey>,
}

/// Return data at the end of a transaction
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct TransactionReturnData {
    /// The program ID that returned the data
    pub program_id: Pubkey,
    /// The binary data returned by the program
    pub data: Vec<u8>,
}

impl TransactionReturnData {
    pub fn new(program_id: Pubkey, data: Vec<u8>) -> Self {
        Self { program_id, data }
    }
}

/// Statistics about accounts loaded during transaction execution.
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
pub struct TransactionLoadedAccountsStats {
    /// The total number of bytes of account data loaded
    pub loaded_accounts_data_size: u32,
    /// The total number of accounts (read or writable) loaded
    pub loaded_accounts_count: usize,
}

impl TransactionLoadedAccountsStats {
    pub fn new(loaded_accounts_data_size: u32, loaded_accounts_count: usize) -> Self {
        Self {
            loaded_accounts_data_size,
            loaded_accounts_count,
        }
    }
}

/// The details of the transaction execution results.
///
/// Contains comprehensive information about a committed transaction,
/// including logs, fees, and any errors that occurred during execution.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct CommittedTransaction {
    /// Error that occurred during transaction execution, if any
    pub transaction_error: Option<TransactionError>,
    /// Optional log messages generated during transaction execution
    /// by Rialo programs
    pub log_messages: Option<Vec<String>>,
    /// Inner instructions executed by the main program via CPI
    #[cfg(feature = "non-pdk")]
    pub inner_instructions: Option<InnerInstructionsList>,
    /// Return data from the main invoked program in the transaction
    pub return_data: Option<TransactionReturnData>,
    /// Number of compute units used by the transaction execution
    pub executed_units: u64,
    /// Fee details for the transaction
    pub fee_details: FeeDetails,
    /// Statistics about accounts loaded during transaction execution
    pub loaded_account_stats: TransactionLoadedAccountsStats,
}

#[cfg(any(test, feature = "testing"))]
impl TransactionExecutionResults {
    /// Creates a new `TransactionExecutionResults` instance for testing purposes.
    ///
    /// This method creates a minimal instance with default values,
    /// primarily used in unit tests.
    ///
    /// # Returns
    ///
    /// A new instance with timestamp 0, block height 0, and default committed transaction
    pub fn new_for_testing() -> Self {
        Self {
            timestamp: 0,
            block_height: 0,
            committed_transaction: CommittedTransaction::default(),
            transaction_index_in_block: 0,
            subscription_pubkey: None,
        }
    }

    /// Creates a new `TransactionExecutionResults` instance for testing with a specific timestamp.
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The unix timestamp to use for the transaction
    ///
    /// # Returns
    ///
    /// A new instance with the specified timestamp, block height 0, and default committed transaction
    pub fn new_for_testing_with_timestamp(timestamp: u64) -> Self {
        Self {
            timestamp,
            block_height: 0,
            committed_transaction: CommittedTransaction::default(),
            transaction_index_in_block: 0,
            subscription_pubkey: None,
        }
    }

    /// Creates a new `TransactionExecutionResults` instance for testing with detailed parameters.
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The unix timestamp for the transaction
    /// * `block_height` - The block height at which the transaction was included
    /// * `log_messages` - Optional log messages generated during execution
    ///
    /// # Returns
    ///
    /// A new instance with the specified parameters and default fee details
    pub fn new_for_testing_with_details(
        timestamp: u64,
        block_height: u64,
        log_messages: Option<Vec<String>>,
    ) -> Self {
        Self {
            timestamp,
            block_height,
            committed_transaction: CommittedTransaction {
                transaction_error: None,
                log_messages,
                #[cfg(feature = "non-pdk")]
                inner_instructions: None,
                return_data: None,
                executed_units: 0,
                fee_details: FeeDetails::default(),
                loaded_account_stats: TransactionLoadedAccountsStats::default(),
            },
            transaction_index_in_block: 0,
            subscription_pubkey: None,
        }
    }
}

/// Metadata for address signatures.
///
/// Contains information about an address (account) within a transaction (signature).
#[derive(Clone, Debug, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
pub struct AddressSignatureMetadata {
    /// Whether this address was marked as writable in the transaction
    pub writeable: bool,
}
