use serde::{Deserialize, Serialize};
use solana_sdk::pubkey::Pubkey;
use solana_transaction_status::EncodedTransactionWithStatusMeta;

/// Filters for the `transactionSubscribe` enhanced WebSocket method.
///
/// Controls which transactions are delivered to the subscription. At minimum, provide
/// [`account_include`](TransactionSubscribeFilter::account_include) to receive transactions
/// involving specific accounts. All fields are optional — omitted fields impose no constraint.
///
/// Up to 50,000 addresses may be specified in each of `account_include`, `account_exclude`,
/// and `account_required`.
#[derive(Debug, Clone, PartialEq, Default, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionSubscribeFilter {
    /// Include or exclude vote-related transactions
    #[serde(skip_serializing_if = "Option::is_none")]
    pub vote: Option<bool>,
    /// Include or exclude transactions that failed
    #[serde(skip_serializing_if = "Option::is_none")]
    pub failed: Option<bool>,
    /// Filter updates to a single transaction by its signature
    #[serde(skip_serializing_if = "Option::is_none")]
    pub signature: Option<String>,
    /// Receive transactions that include **any** of these accounts (OR logic).
    /// Only one of the listed accounts needs to appear in the transaction.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub account_include: Option<Vec<String>>,
    /// Exclude transactions that involve any of these accounts
    #[serde(skip_serializing_if = "Option::is_none")]
    pub account_exclude: Option<Vec<String>>,
    /// Transactions must include **all** of these accounts to match (AND logic)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub account_required: Option<Vec<String>>,
}

impl TransactionSubscribeFilter {
    /// Creates a filter that subscribes to all transactions involving the given public key.
    pub fn standard(key: &Pubkey) -> Self {
        Self {
            account_include: Some(vec![key.to_string()]),
            ..Default::default()
        }
    }
}

/// The encoding format for transaction data returned by the enhanced WebSocket.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum UiEnhancedTransactionEncoding {
    Base58,
    Base64,
    #[serde(rename = "base64+zstd")]
    Base64Zstd,
    JsonParsed,
}

/// The commitment level for transaction subscription notifications.
///
/// Determines how finalized a transaction must be before it is delivered to the subscriber.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum TransactionCommitment {
    /// The transaction has been received by the leader and is being processed
    Processed,
    /// The transaction has been included in a block that has reached supermajority confirmation
    Confirmed,
    /// The transaction is in a block that has been finalized (maximum confirmation)
    Finalized,
}

/// The level of detail included in transaction subscription notifications.
///
/// Note: `Accounts` and `Full` require
/// [`max_supported_transaction_version`](TransactionSubscribeOptions::max_supported_transaction_version)
/// to be set (typically to `0`).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum TransactionDetails {
    /// Include full transaction data with all instructions and metadata
    Full,
    /// Include only the transaction signatures
    Signatures,
    /// Include only the list of accounts involved
    Accounts,
    /// Include no transaction data (notification only)
    None,
}

/// Optional configuration for a `transactionSubscribe` subscription.
///
/// Controls the commitment level, encoding format, detail level, and other parameters for
/// transaction notifications. All fields are optional — the [`Default`] implementation provides
/// sensible defaults (`confirmed`, `jsonParsed`, `full`, rewards shown, version `0`).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionSubscribeOptions {
    /// The commitment level for notifications (`processed`, `confirmed`, or `finalized`)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub commitment: Option<TransactionCommitment>,
    /// The encoding format for returned transaction data
    #[serde(skip_serializing_if = "Option::is_none")]
    pub encoding: Option<UiEnhancedTransactionEncoding>,
    /// The level of detail for returned transaction data
    #[serde(skip_serializing_if = "Option::is_none")]
    pub transaction_details: Option<TransactionDetails>,
    /// Whether reward data should be included in the notifications
    #[serde(skip_serializing_if = "Option::is_none")]
    pub show_rewards: Option<bool>,
    /// The highest transaction version to receive. Set to `0` for both legacy and v0 transactions.
    /// Required when `transaction_details` is `Full` or `Accounts`.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_supported_transaction_version: Option<u8>,
}

impl Default for TransactionSubscribeOptions {
    fn default() -> Self {
        Self {
            commitment: Some(TransactionCommitment::Confirmed),
            encoding: Some(UiEnhancedTransactionEncoding::JsonParsed),
            transaction_details: Some(TransactionDetails::Full),
            show_rewards: Some(true),
            max_supported_transaction_version: Some(0),
        }
    }
}

/// Combined filter and options for a `transactionSubscribe` call.
///
/// Passed to [`EnhancedWebsocket::transaction_subscribe`](crate::websocket::EnhancedWebsocket::transaction_subscribe).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcTransactionsConfig {
    /// The filter criteria that determine which transactions are delivered
    pub filter: TransactionSubscribeFilter,
    /// Optional configuration for commitment, encoding, and detail level
    pub options: TransactionSubscribeOptions,
}

/// A real-time transaction notification received from a `transactionSubscribe` subscription.
///
/// Delivered whenever a transaction matching the subscription filter is observed at the
/// configured commitment level.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionNotification {
    /// The full encoded transaction with status metadata
    pub transaction: EncodedTransactionWithStatusMeta,
    /// The transaction signature (base-58 encoded)
    pub signature: String,
    /// The slot in which the transaction was processed
    pub slot: u64,
}
