#![cfg_attr(feature = "frozen-abi", feature(min_specialization))]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
//! The Solana [`Account`] type.

use std::{
    cell::{Ref, RefCell},
    fmt,
    mem::MaybeUninit,
    ptr,
    rc::Rc,
    sync::Arc,
};

#[cfg(feature = "dev-context-only-utils")]
use qualifier_attr::qualifiers;
#[cfg(feature = "frozen-abi")]
use rialo_frozen_abi_macro::{frozen_abi, AbiExample};
use rialo_rex_wasm_v1_loader_interface::{BytecodeFormat, ExecutionContext};
use rialo_s_account_info::{debug_account_data::*, AccountInfo};
use rialo_s_clock::{Epoch, INITIAL_RENT_EPOCH};
use rialo_s_instruction::error::{InstructionError, KelvinsError};
use rialo_s_pubkey::Pubkey;
use rialo_s_sdk_ids::{
    bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, loader_v4, native_loader,
    rex_wasm_v1_loader, risc_v_loader,
};
#[cfg(feature = "bincode")]
use rialo_s_sysvar::Sysvar;
#[cfg(feature = "serde")]
use serde::ser::{Serialize, Serializer};
#[cfg(feature = "bincode")]
pub mod state_traits;

/// Errors that can occur when operating on accounts.
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum AccountError {
    /// Attempted to perform a data operation on a BalanceAccount, which has no data.
    #[error("Balance accounts do not have data")]
    BalanceAccountHasNoData,
    /// Invalid owner slice length (expected 32 bytes).
    #[error("Invalid owner slice length: expected {expected} bytes, got {actual}")]
    InvalidOwnerSliceLength { expected: usize, actual: usize },
    /// Attempted to mutate an ExecutableAccount, which is immutable.
    #[error("Executable accounts are immutable and cannot be modified")]
    ExecutableAccountIsImmutable,
    /// The account owner is not a valid program loader for executable accounts.
    #[error("Account owner {owner} is not a valid program loader for executable accounts")]
    InvalidExecutableOwner { owner: Pubkey },
}

impl From<AccountError> for InstructionError {
    fn from(error: AccountError) -> Self {
        match error {
            AccountError::BalanceAccountHasNoData => InstructionError::InvalidAccountData,
            AccountError::InvalidOwnerSliceLength { .. } => InstructionError::InvalidArgument,
            AccountError::ExecutableAccountIsImmutable => InstructionError::ReadonlyDataModified,
            AccountError::InvalidExecutableOwner { .. } => InstructionError::InvalidAccountOwner,
        }
    }
}

/// An Account with data that is stored on chain
#[repr(C)]
#[cfg_attr(
    feature = "frozen-abi",
    derive(AbiExample),
    frozen_abi(digest = "2SUJNHbXMPWrsSXmDTFc4VHx2XQ85fT5Leabefh5Nwe7")
)]
#[cfg_attr(
    feature = "serde",
    derive(serde_derive::Deserialize),
    serde(rename_all = "camelCase")
)]
#[derive(PartialEq, Eq, Clone, Default)]
pub struct Account {
    /// kelvins in the account
    pub kelvins: u64,
    /// data held in this account
    #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
    pub data: Vec<u8>,
    /// the program that owns this account
    pub owner: Pubkey,
    /// the epoch at which this account will next owe rent
    pub rent_epoch: Epoch,
}

// mod because we need 'Account' below to have the name 'Account' to match expected serialization
#[cfg(feature = "serde")]
#[allow(elided_lifetimes_in_paths)] // false positive
mod account_serialize {
    #[cfg(feature = "frozen-abi")]
    use rialo_frozen_abi_macro::{frozen_abi, AbiExample};
    use rialo_s_clock::Epoch;
    use rialo_s_pubkey::Pubkey;
    use serde::{ser::Serializer, Serialize};

    use crate::ReadableAccount;
    #[repr(C)]
    #[cfg_attr(
        feature = "frozen-abi",
        derive(AbiExample),
        frozen_abi(digest = "2SUJNHbXMPWrsSXmDTFc4VHx2XQ85fT5Leabefh5Nwe7")
    )]
    #[derive(serde_derive::Serialize)]
    #[serde(rename_all = "camelCase")]
    struct Account<'a> {
        kelvins: u64,
        #[serde(with = "serde_bytes")]
        // a slice so we don't have to make a copy just to serialize this
        data: &'a [u8],
        owner: &'a Pubkey,
        rent_epoch: Epoch,
    }

    /// allows us to implement serialize on AccountSharedData that is equivalent to Account::serialize without making a copy of the Vec<u8>
    pub fn serialize_account<S>(
        account: &impl ReadableAccount,
        serializer: S,
    ) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let temp = Account {
            kelvins: account.kelvins(),
            data: account.data(),
            owner: account.owner(),
            rent_epoch: account.rent_epoch(),
        };
        temp.serialize(serializer)
    }
}

#[cfg(feature = "serde")]
impl Serialize for Account {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        crate::account_serialize::serialize_account(self, serializer)
    }
}

#[cfg(feature = "serde")]
impl Serialize for AccountSharedData {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        crate::account_serialize::serialize_account(self, serializer)
    }
}

/// An Account with data that is stored on chain
/// This will be the in-memory representation of the 'Account' struct data.
/// The existing 'Account' structure cannot easily change due to downstream projects.
#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
#[cfg_attr(
    feature = "serde",
    derive(serde_derive::Deserialize),
    serde(from = "Account")
)]
#[derive(PartialEq, Eq, Clone, Default)]
pub struct AccountSharedData {
    /// kelvins in the account
    kelvins: u64,
    /// data held in this account
    data: Arc<Vec<u8>>,
    /// the program that owns this account
    owner: Pubkey,
    /// the epoch at which this account will next owe rent
    rent_epoch: Epoch,
}

/// A simple account that only holds balance information.
///
/// Unlike [`Account`] and [`AccountSharedData`], this type does not store
/// account data or an executable flag. It is useful for accounts that only
/// need to track ownership and balance (kelvins).
#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
#[cfg_attr(
    feature = "serde",
    derive(serde_derive::Serialize, serde_derive::Deserialize),
    serde(rename_all = "camelCase")
)]
#[derive(Debug, PartialEq, Eq, Clone, Default)]
pub struct BalanceAccount {
    /// kelvins in the account
    pub kelvins: u64,
    /// the program that owns this account
    pub owner: Pubkey,
    /// the epoch at which this account will next owe rent
    pub rent_epoch: Epoch,
}

impl BalanceAccount {
    /// Create a new `BalanceAccount` with the given balance and owner.
    pub fn new(kelvins: u64, owner: &Pubkey) -> Self {
        Self {
            kelvins,
            owner: *owner,
            rent_epoch: Epoch::default(),
        }
    }

    /// Create a new `BalanceAccount` with the given balance, owner, and rent epoch.
    pub fn new_with_rent_epoch(kelvins: u64, owner: &Pubkey, rent_epoch: Epoch) -> Self {
        Self {
            kelvins,
            owner: *owner,
            rent_epoch,
        }
    }
}

impl ReadableAccount for BalanceAccount {
    fn kelvins(&self) -> u64 {
        self.kelvins
    }

    fn data(&self) -> &[u8] {
        // BalanceAccount has no data
        &[]
    }

    fn owner(&self) -> &Pubkey {
        &self.owner
    }

    fn rent_epoch(&self) -> Epoch {
        self.rent_epoch
    }
}

impl From<&AccountSharedData> for BalanceAccount {
    fn from(account: &AccountSharedData) -> Self {
        Self {
            kelvins: account.kelvins(),
            owner: *account.owner(),
            rent_epoch: account.rent_epoch(),
        }
    }
}

impl From<AccountSharedData> for BalanceAccount {
    fn from(account: AccountSharedData) -> Self {
        Self::from(&account)
    }
}

impl From<&Account> for BalanceAccount {
    fn from(account: &Account) -> Self {
        Self {
            kelvins: account.kelvins,
            owner: account.owner,
            rent_epoch: account.rent_epoch,
        }
    }
}

impl From<Account> for BalanceAccount {
    fn from(account: Account) -> Self {
        Self::from(&account)
    }
}

/// An executable account that contains program data.
///
/// This type represents accounts that store executable programs. Unlike regular
/// data accounts, executable accounts are immutable and contain program bytecode.
#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
#[cfg_attr(
    feature = "serde",
    derive(serde_derive::Serialize, serde_derive::Deserialize),
    serde(rename_all = "camelCase")
)]
#[derive(Debug, PartialEq, Eq, Clone, Default)]
pub struct ExecutableAccount {
    /// kelvins in the account
    pub kelvins: u64,
    /// program data held in this account
    pub data: Arc<Vec<u8>>,
    /// the program that owns this account (the loader program)
    pub owner: Pubkey,
    /// the epoch at which this account will next owe rent
    pub rent_epoch: Epoch,
}

impl ExecutableAccount {
    /// Create a new `ExecutableAccount` with the given parameters.
    pub fn new(kelvins: u64, data: Vec<u8>, owner: &Pubkey, rent_epoch: Epoch) -> Self {
        Self {
            kelvins,
            data: Arc::new(data),
            owner: *owner,
            rent_epoch,
        }
    }

    /// Create a new `ExecutableAccount` with default rent epoch.
    pub fn new_with_data(kelvins: u64, data: Vec<u8>, owner: &Pubkey) -> Self {
        Self::new(kelvins, data, owner, Epoch::default())
    }

    /// Returns the bytecode format based on the loader owner.
    ///
    /// This method determines the VM format of the bytecode by checking the owner:
    /// - If owned by RexWasmV1Loader → returns `BytecodeFormat::Wasm`
    /// - Otherwise (legacy loaders) → returns `BytecodeFormat::PolkaVm`
    pub fn bytecode_format(&self) -> BytecodeFormat {
        if self.owner == rex_wasm_v1_loader::id() {
            rialo_rex_wasm_v1_loader_interface::BYTECODE_FORMAT // Wasm
        } else {
            BytecodeFormat::PolkaVm // Default for legacy loaders
        }
    }

    /// Returns the execution context based on the loader owner.
    ///
    /// This method determines where the code executes by checking the owner:
    /// - If owned by RexWasmV1Loader → returns `ExecutionContext::Rex` (off-chain TEE)
    /// - Otherwise (legacy loaders) → returns `ExecutionContext::Chain` (on-chain SVM)
    pub fn execution_context(&self) -> ExecutionContext {
        if self.owner == rex_wasm_v1_loader::id() {
            rialo_rex_wasm_v1_loader_interface::EXECUTION_CONTEXT // Rex
        } else {
            ExecutionContext::Chain // Default for legacy loaders
        }
    }
}

impl ReadableAccount for ExecutableAccount {
    fn kelvins(&self) -> u64 {
        self.kelvins
    }

    fn data(&self) -> &[u8] {
        &self.data
    }

    fn owner(&self) -> &Pubkey {
        &self.owner
    }

    fn rent_epoch(&self) -> Epoch {
        self.rent_epoch
    }
}

/// Enum representing the different types of accounts that can be stored on disk.
///
/// This enum is used during serialization to disk to discriminate between
/// different account types, allowing for more efficient storage of simple
/// balance-only accounts and separating executable program accounts.
///
/// # Usage Patterns
///
/// **Prefer `StoredAccount` over `AccountSharedData`** in the following scenarios:
///
/// 1. **Transaction Processing**: Use `StoredAccount` when loading, processing, and
///    storing accounts during transaction execution. This preserves the executable
///    status correctly through the `Executable` variant.
///
/// 2. **Account Storage**: Always use `StoredAccount` when storing accounts to disk
///    or in memory caches. The variant discriminates account types for efficient
///    storage.
///
/// 3. **Creating Executable Accounts**: When creating program accounts, use
///    `StoredAccount::Executable(ExecutableAccount::new(...))` directly:
///    ```ignore
///    let program_account = StoredAccount::Executable(
///        ExecutableAccount::new(kelvins, bytecode, &owner, rent_epoch)
///    );
///    ```
///
/// **Use `AccountSharedData` conversion** only when required by legacy APIs:
///
/// 1. **Genesis Config**: The `GenesisConfig::add_account()` API requires
///    `AccountSharedData` for backward compatibility.
///
#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
#[cfg_attr(
    feature = "serde",
    derive(serde_derive::Serialize, serde_derive::Deserialize)
)]
#[repr(u16)]
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum StoredAccount {
    /// A non-executable account with data.
    Data(AccountSharedData),
    /// A simple balance-only account without data.
    Balance(BalanceAccount),
    /// An executable account containing program bytecode.
    Executable(ExecutableAccount),
}

impl Default for StoredAccount {
    fn default() -> Self {
        StoredAccount::Data(AccountSharedData::default())
    }
}

impl StoredAccount {
    /// Returns true if this is a `Data` variant.
    pub fn is_data(&self) -> bool {
        matches!(self, StoredAccount::Data(_))
    }

    /// Returns true if this is a `Balance` variant.
    pub fn is_balance(&self) -> bool {
        matches!(self, StoredAccount::Balance(_))
    }

    /// Returns true if this is an `Executable` variant.
    pub fn executable(&self) -> bool {
        matches!(self, StoredAccount::Executable(_))
    }

    /// Returns a reference to the inner `AccountSharedData` if this is a `Data` variant.
    pub fn as_data(&self) -> Option<&AccountSharedData> {
        match self {
            StoredAccount::Data(account) => Some(account),
            _ => None,
        }
    }

    /// Returns a mutable reference to the inner `AccountSharedData` if this is a `Data` variant.
    ///
    /// This is useful for operations like rent collection that need to mutate the account.
    pub fn as_data_mut(&mut self) -> Option<&mut AccountSharedData> {
        match self {
            StoredAccount::Data(account) => Some(account),
            _ => None,
        }
    }

    /// Returns a reference to the inner `BalanceAccount` if this is a `Balance` variant.
    pub fn as_balance(&self) -> Option<&BalanceAccount> {
        match self {
            StoredAccount::Balance(balance) => Some(balance),
            _ => None,
        }
    }

    /// Returns a reference to the inner `ExecutableAccount` if this is an `Executable` variant.
    pub fn as_executable(&self) -> Option<&ExecutableAccount> {
        match self {
            StoredAccount::Executable(executable) => Some(executable),
            _ => None,
        }
    }

    /// Returns the capacity of the account's data.
    pub fn capacity(&self) -> usize {
        match self {
            StoredAccount::Data(account) => account.capacity(),
            StoredAccount::Balance(_) => 0,
            StoredAccount::Executable(executable) => executable.data.capacity(),
        }
    }

    /// Returns a clone of the account's data as an Arc.
    pub fn data_clone(&self) -> Arc<Vec<u8>> {
        match self {
            StoredAccount::Data(account) => account.data_clone(),
            StoredAccount::Balance(_) => Arc::new(Vec::new()),
            StoredAccount::Executable(executable) => Arc::clone(&executable.data),
        }
    }

    /// Deserializes the account data.
    #[cfg(feature = "bincode")]
    pub fn deserialize_data<T: serde::de::DeserializeOwned>(&self) -> Result<T, bincode::Error> {
        bincode::deserialize(self.data())
    }

    /// Converts this `StoredAccount` to an `Executable` variant.
    ///
    /// This is used when an account becomes executable during transaction execution
    /// (e.g., after a program deployment via LoaderV4).
    ///
    /// # Errors
    /// - Returns `AccountError::BalanceAccountHasNoData` if called on a Balance account.
    /// - Returns `AccountError::InvalidExecutableOwner` if the account owner is not a valid
    ///   program loader (not in `PROGRAM_OWNERS`).
    pub fn to_executable_account(self) -> Result<Self, AccountError> {
        match self {
            StoredAccount::Executable(_) => Ok(self), // Already executable
            StoredAccount::Data(account) => {
                let owner = *account.owner();
                if !PROGRAM_OWNERS.contains(&owner) {
                    return Err(AccountError::InvalidExecutableOwner { owner });
                }
                Ok(StoredAccount::Executable(ExecutableAccount {
                    kelvins: account.kelvins(),
                    data: account.data_clone(),
                    owner,
                    rent_epoch: account.rent_epoch(),
                }))
            }
            StoredAccount::Balance(_) => Err(AccountError::BalanceAccountHasNoData),
        }
    }

    /// Converts this `StoredAccount` to a `Data` variant.
    ///
    /// This is used when an account loses its executable status.
    /// For `Balance` accounts, this creates a `Data` account with empty data.
    pub fn to_data_account(self) -> Self {
        match self {
            StoredAccount::Data(_) => self, // Already data
            StoredAccount::Executable(executable) => {
                let data = Arc::try_unwrap(executable.data).unwrap_or_else(|arc| (*arc).clone());
                StoredAccount::Data(AccountSharedData::create(
                    executable.kelvins,
                    data,
                    executable.owner,
                    executable.rent_epoch,
                ))
            }
            StoredAccount::Balance(balance) => StoredAccount::Data(AccountSharedData::create(
                balance.kelvins,
                Vec::new(),
                balance.owner,
                balance.rent_epoch,
            )),
        }
    }

    /// Sets the executable status, potentially converting the variant.
    ///
    /// When setting to true, converts to `Executable` variant if not already.
    /// When setting to false, converts to `Data` variant if currently `Executable`.
    pub fn set_executable(&mut self, executable: bool) -> Result<(), AccountError> {
        if executable && !self.executable() {
            // Clone before taking ownership so we can restore on failure
            let backup = self.clone();
            match std::mem::take(self).to_executable_account() {
                Ok(converted) => *self = converted,
                Err(e) => {
                    // Conversion failed - restore the original value
                    *self = backup;
                    return Err(e);
                }
            }
        } else if !executable && self.executable() {
            *self = std::mem::take(self).to_data_account();
        }
        Ok(())
    }
}

impl ReadableAccount for StoredAccount {
    fn kelvins(&self) -> u64 {
        match self {
            StoredAccount::Data(account) => account.kelvins(),
            StoredAccount::Balance(balance) => balance.kelvins(),
            StoredAccount::Executable(executable) => executable.kelvins(),
        }
    }

    fn data(&self) -> &[u8] {
        match self {
            StoredAccount::Data(account) => account.data(),
            StoredAccount::Balance(balance) => balance.data(),
            StoredAccount::Executable(executable) => executable.data(),
        }
    }

    fn owner(&self) -> &Pubkey {
        match self {
            StoredAccount::Data(account) => account.owner(),
            StoredAccount::Balance(balance) => balance.owner(),
            StoredAccount::Executable(executable) => executable.owner(),
        }
    }

    fn rent_epoch(&self) -> Epoch {
        match self {
            StoredAccount::Data(account) => account.rent_epoch(),
            StoredAccount::Balance(balance) => balance.rent_epoch(),
            StoredAccount::Executable(executable) => executable.rent_epoch(),
        }
    }
}

impl From<BalanceAccount> for StoredAccount {
    fn from(balance: BalanceAccount) -> Self {
        StoredAccount::Balance(balance)
    }
}

impl From<ExecutableAccount> for StoredAccount {
    fn from(executable: ExecutableAccount) -> Self {
        StoredAccount::Executable(executable)
    }
}

impl From<AccountSharedData> for StoredAccount {
    fn from(account: AccountSharedData) -> Self {
        StoredAccount::Data(account)
    }
}

impl From<Account> for StoredAccount {
    fn from(account: Account) -> Self {
        StoredAccount::Data(AccountSharedData::from(account))
    }
}

impl From<StoredAccount> for AccountSharedData {
    fn from(stored: StoredAccount) -> Self {
        match stored {
            StoredAccount::Data(account) => account, // No clone needed - take ownership
            StoredAccount::Balance(balance) => AccountSharedData::create(
                balance.kelvins,
                Vec::new(),
                balance.owner,
                balance.rent_epoch,
            ),
            StoredAccount::Executable(executable) => AccountSharedData {
                kelvins: executable.kelvins,
                data: executable.data, // Takes ownership of Arc
                owner: executable.owner,
                rent_epoch: executable.rent_epoch,
            },
        }
    }
}

impl StoredAccount {
    /// Creates a StoredAccount::Executable from an AccountSharedData.
    /// This should be used when loading an account that is known to be executable.
    ///
    /// This method consumes the AccountSharedData and extracts the Arc directly,
    /// avoiding an extra Arc clone.
    pub fn into_executable(account: AccountSharedData) -> Self {
        let (kelvins, data, owner, rent_epoch) = account.into_parts();
        StoredAccount::Executable(ExecutableAccount {
            kelvins,
            data,
            owner,
            rent_epoch,
        })
    }

    /// Creates a new `StoredAccount::Data` with the specified parameters.
    ///
    /// This is the preferred way to create data accounts. Use this instead of
    /// creating `AccountSharedData` and converting to `StoredAccount`.
    ///
    /// # Arguments
    /// * `kelvins` - The balance of the account
    /// * `space` - The size of the data buffer to allocate
    /// * `owner` - The program that owns this account
    ///
    /// # Example
    /// ```ignore
    /// // GOOD: Create StoredAccount directly
    /// let account = StoredAccount::new(1000, 128, &owner_pubkey);
    ///
    /// // AVOID: Creating AccountSharedData first
    /// let account = StoredAccount::Data(AccountSharedData::new(1000, 128, &owner_pubkey));
    /// ```
    pub fn new(kelvins: u64, space: usize, owner: &Pubkey) -> Self {
        StoredAccount::Data(AccountSharedData::new(kelvins, space, owner))
    }

    /// Creates a new `StoredAccount::Balance` with the specified parameters.
    ///
    /// Use this for accounts that only need to hold kelvins without any data.
    ///
    /// # Arguments
    /// * `kelvins` - The balance of the account
    /// * `owner` - The program that owns this account
    pub fn new_balance(kelvins: u64, owner: &Pubkey) -> Self {
        StoredAccount::Balance(BalanceAccount::new(kelvins, owner))
    }

    /// Creates a new `StoredAccount::Executable` with the specified parameters.
    ///
    /// Use this for program accounts that contain executable bytecode.
    ///
    /// # Arguments
    /// * `kelvins` - The balance of the account
    /// * `data` - The program bytecode
    /// * `owner` - The program loader that owns this account (e.g., BPF loader)
    pub fn new_executable(kelvins: u64, data: Vec<u8>, owner: &Pubkey) -> Self {
        StoredAccount::Executable(ExecutableAccount::new_with_data(kelvins, data, owner))
    }

    /// Creates a new `StoredAccount::Data` with initial data.
    ///
    /// # Arguments
    /// * `kelvins` - The balance of the account
    /// * `data` - The initial data to store
    /// * `owner` - The program that owns this account
    pub fn new_with_data(kelvins: u64, data: Vec<u8>, owner: &Pubkey) -> Self {
        StoredAccount::Data(AccountSharedData::create(kelvins, data, *owner, 0))
    }

    /// Sets the kelvins for this account.
    pub fn set_kelvins(&mut self, kelvins: u64) {
        match self {
            StoredAccount::Data(account) => account.set_kelvins(kelvins),
            StoredAccount::Balance(balance) => balance.kelvins = kelvins,
            StoredAccount::Executable(executable) => executable.kelvins = kelvins,
        }
    }

    /// Adds kelvins to this account, checking for overflow.
    pub fn checked_add_kelvins(&mut self, kelvins: u64) -> Result<(), KelvinsError> {
        let current = self.kelvins();
        let new_kelvins = current
            .checked_add(kelvins)
            .ok_or(KelvinsError::ArithmeticOverflow)?;
        self.set_kelvins(new_kelvins);
        Ok(())
    }

    /// Subtracts kelvins from this account, checking for underflow.
    pub fn checked_sub_kelvins(&mut self, kelvins: u64) -> Result<(), KelvinsError> {
        let current = self.kelvins();
        let new_kelvins = current
            .checked_sub(kelvins)
            .ok_or(KelvinsError::ArithmeticUnderflow)?;
        self.set_kelvins(new_kelvins);
        Ok(())
    }

    /// Returns a mutable slice of the account's data.
    ///
    /// # Errors
    /// Returns `AccountError::BalanceAccountHasNoData` if called on a Balance account.
    pub fn try_data_as_mut_slice(&mut self) -> Result<&mut [u8], AccountError> {
        match self {
            StoredAccount::Data(account) => Ok(account.data_as_mut_slice()),
            StoredAccount::Balance(_) => Err(AccountError::BalanceAccountHasNoData),
            StoredAccount::Executable(executable) => {
                Ok(Arc::make_mut(&mut executable.data).as_mut_slice())
            }
        }
    }

    /// Sets the owner of this account.
    pub fn set_owner(&mut self, owner: Pubkey) {
        match self {
            StoredAccount::Data(account) => account.set_owner(owner),
            StoredAccount::Balance(balance) => balance.owner = owner,
            StoredAccount::Executable(executable) => executable.owner = owner,
        }
    }

    /// Copies the owner from a slice.
    ///
    /// # Errors
    /// Returns `AccountError::InvalidOwnerSliceLength` if source is not exactly 32 bytes.
    pub fn try_copy_into_owner_from_slice(&mut self, source: &[u8]) -> Result<(), AccountError> {
        const PUBKEY_LEN: usize = 32;
        if source.len() != PUBKEY_LEN {
            return Err(AccountError::InvalidOwnerSliceLength {
                expected: PUBKEY_LEN,
                actual: source.len(),
            });
        }
        match self {
            StoredAccount::Data(account) => account.copy_into_owner_from_slice(source),
            StoredAccount::Balance(balance) => {
                balance.owner.as_mut().copy_from_slice(source);
            }
            StoredAccount::Executable(executable) => {
                executable.owner.as_mut().copy_from_slice(source);
            }
        }
        Ok(())
    }

    /// Sets the rent epoch for this account.
    pub fn set_rent_epoch(&mut self, epoch: Epoch) {
        match self {
            StoredAccount::Data(account) => account.set_rent_epoch(epoch),
            StoredAccount::Balance(balance) => balance.rent_epoch = epoch,
            StoredAccount::Executable(executable) => executable.rent_epoch = epoch,
        }
    }

    /// Returns true if the data is shared (has multiple references).
    pub fn is_shared(&self) -> bool {
        match self {
            StoredAccount::Data(account) => account.is_shared(),
            StoredAccount::Balance(_) => false,
            StoredAccount::Executable(executable) => Arc::strong_count(&executable.data) > 1,
        }
    }

    /// Returns a mutable reference to spare capacity after the data.
    ///
    /// # Errors
    /// Returns `AccountError::BalanceAccountHasNoData` if called on a Balance account.
    pub fn try_spare_data_capacity_mut(&mut self) -> Result<&mut [MaybeUninit<u8>], AccountError> {
        match self {
            StoredAccount::Data(account) => Ok(account.spare_data_capacity_mut()),
            StoredAccount::Balance(_) => Err(AccountError::BalanceAccountHasNoData),
            StoredAccount::Executable(executable) => {
                Ok(Arc::make_mut(&mut executable.data).spare_capacity_mut())
            }
        }
    }

    /// Sets the data from a slice.
    ///
    /// # Errors
    /// Returns `AccountError::BalanceAccountHasNoData` if called on a Balance account.
    pub fn try_set_data_from_slice(&mut self, data: &[u8]) -> Result<(), AccountError> {
        match self {
            StoredAccount::Data(account) => {
                account.set_data_from_slice(data);
                Ok(())
            }
            StoredAccount::Balance(_) => Err(AccountError::BalanceAccountHasNoData),
            StoredAccount::Executable(executable) => {
                let inner = Arc::make_mut(&mut executable.data);
                inner.clear();
                inner.extend_from_slice(data);
                Ok(())
            }
        }
    }

    /// Resizes the account data.
    ///
    /// # Errors
    /// Returns `AccountError::BalanceAccountHasNoData` if called on a Balance account.
    pub fn try_resize(&mut self, new_len: usize, value: u8) -> Result<(), AccountError> {
        match self {
            StoredAccount::Data(account) => {
                account.resize(new_len, value);
                Ok(())
            }
            StoredAccount::Balance(_) => Err(AccountError::BalanceAccountHasNoData),
            StoredAccount::Executable(executable) => {
                Arc::make_mut(&mut executable.data).resize(new_len, value);
                Ok(())
            }
        }
    }

    /// Extends the account data from a slice.
    ///
    /// # Errors
    /// Returns `AccountError::BalanceAccountHasNoData` if called on a Balance account.
    pub fn try_extend_from_slice(&mut self, data: &[u8]) -> Result<(), AccountError> {
        match self {
            StoredAccount::Data(account) => {
                account.extend_from_slice(data);
                Ok(())
            }
            StoredAccount::Balance(_) => Err(AccountError::BalanceAccountHasNoData),
            StoredAccount::Executable(executable) => {
                Arc::make_mut(&mut executable.data).extend_from_slice(data);
                Ok(())
            }
        }
    }

    /// Reserves additional capacity for the account data.
    pub fn reserve(&mut self, additional: usize) {
        match self {
            StoredAccount::Data(account) => account.reserve(additional),
            StoredAccount::Balance(_) => {} // No-op for Balance accounts
            StoredAccount::Executable(executable) => {
                if let Some(data) = Arc::get_mut(&mut executable.data) {
                    data.reserve(additional);
                }
            }
        }
    }

    /// Sets the account data from a Vec.
    ///
    /// # Errors
    /// Returns `AccountError::BalanceAccountHasNoData` if called on a Balance account.
    #[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
    pub fn try_set_data(&mut self, data: Vec<u8>) -> Result<(), AccountError> {
        match self {
            StoredAccount::Data(account) => {
                account.set_data(data);
                Ok(())
            }
            StoredAccount::Balance(_) => Err(AccountError::BalanceAccountHasNoData),
            StoredAccount::Executable(executable) => {
                executable.data = Arc::new(data);
                Ok(())
            }
        }
    }
}

/// Compares two ReadableAccounts for equality.
///
/// Returns true if all fields (kelvins, rent_epoch, owner, data) are equal.
/// Note: This does not compare executable status - use `StoredAccount::is_executable()`
/// to check executable status separately.
pub fn accounts_equal<T: ReadableAccount, U: ReadableAccount>(me: &T, other: &U) -> bool {
    me.kelvins() == other.kelvins()
        && me.rent_epoch() == other.rent_epoch()
        && me.owner() == other.owner()
        && me.data() == other.data()
}

impl From<AccountSharedData> for Account {
    fn from(mut other: AccountSharedData) -> Self {
        let account_data = Arc::make_mut(&mut other.data);
        Self {
            kelvins: other.kelvins,
            data: std::mem::take(account_data),
            owner: other.owner,
            rent_epoch: other.rent_epoch,
        }
    }
}

impl From<StoredAccount> for Account {
    fn from(stored: StoredAccount) -> Self {
        // For Data variant, use the efficient AccountSharedData -> Account conversion
        // which avoids copying if the Arc has refcount of 1.
        // For other variants, we must copy the data.
        match stored {
            StoredAccount::Data(account) => Account::from(account),
            StoredAccount::Balance(balance) => Self {
                kelvins: balance.kelvins,
                data: Vec::new(),
                owner: balance.owner,
                rent_epoch: balance.rent_epoch,
            },
            StoredAccount::Executable(mut executable) => {
                // Try to take ownership of the data if Arc refcount is 1
                let data = Arc::make_mut(&mut executable.data);
                Self {
                    kelvins: executable.kelvins,
                    data: std::mem::take(data),
                    owner: executable.owner,
                    rent_epoch: executable.rent_epoch,
                }
            }
        }
    }
}

impl From<Account> for AccountSharedData {
    fn from(other: Account) -> Self {
        Self {
            kelvins: other.kelvins,
            data: Arc::new(other.data),
            owner: other.owner,
            rent_epoch: other.rent_epoch,
        }
    }
}

pub trait WritableAccount: ReadableAccount {
    fn set_kelvins(&mut self, kelvins: u64);
    fn checked_add_kelvins(&mut self, kelvins: u64) -> Result<(), KelvinsError> {
        self.set_kelvins(
            self.kelvins()
                .checked_add(kelvins)
                .ok_or(KelvinsError::ArithmeticOverflow)?,
        );
        Ok(())
    }
    fn checked_sub_kelvins(&mut self, kelvins: u64) -> Result<(), KelvinsError> {
        self.set_kelvins(
            self.kelvins()
                .checked_sub(kelvins)
                .ok_or(KelvinsError::ArithmeticUnderflow)?,
        );
        Ok(())
    }
    fn saturating_add_kelvins(&mut self, kelvins: u64) {
        self.set_kelvins(self.kelvins().saturating_add(kelvins))
    }
    fn saturating_sub_kelvins(&mut self, kelvins: u64) {
        self.set_kelvins(self.kelvins().saturating_sub(kelvins))
    }
    fn data_as_mut_slice(&mut self) -> &mut [u8];
    fn set_owner(&mut self, owner: Pubkey);
    fn copy_into_owner_from_slice(&mut self, source: &[u8]);
    fn set_rent_epoch(&mut self, epoch: Epoch);
    fn create(kelvins: u64, data: Vec<u8>, owner: Pubkey, rent_epoch: Epoch) -> Self;
}

pub trait ReadableAccount {
    fn kelvins(&self) -> u64;
    fn data(&self) -> &[u8];
    fn owner(&self) -> &Pubkey;
    fn rent_epoch(&self) -> Epoch;
}

impl ReadableAccount for Account {
    fn kelvins(&self) -> u64 {
        self.kelvins
    }
    fn data(&self) -> &[u8] {
        &self.data
    }
    fn owner(&self) -> &Pubkey {
        &self.owner
    }
    fn rent_epoch(&self) -> Epoch {
        self.rent_epoch
    }
}

impl WritableAccount for Account {
    fn set_kelvins(&mut self, kelvins: u64) {
        self.kelvins = kelvins;
    }
    fn data_as_mut_slice(&mut self) -> &mut [u8] {
        &mut self.data
    }
    fn set_owner(&mut self, owner: Pubkey) {
        self.owner = owner;
    }
    fn copy_into_owner_from_slice(&mut self, source: &[u8]) {
        self.owner.as_mut().copy_from_slice(source);
    }
    fn set_rent_epoch(&mut self, epoch: Epoch) {
        self.rent_epoch = epoch;
    }
    fn create(kelvins: u64, data: Vec<u8>, owner: Pubkey, rent_epoch: Epoch) -> Self {
        Account {
            kelvins,
            data,
            owner,
            rent_epoch,
        }
    }
}

impl WritableAccount for AccountSharedData {
    fn set_kelvins(&mut self, kelvins: u64) {
        self.kelvins = kelvins;
    }
    fn data_as_mut_slice(&mut self) -> &mut [u8] {
        &mut self.data_mut()[..]
    }
    fn set_owner(&mut self, owner: Pubkey) {
        self.owner = owner;
    }
    fn copy_into_owner_from_slice(&mut self, source: &[u8]) {
        self.owner.as_mut().copy_from_slice(source);
    }
    fn set_rent_epoch(&mut self, epoch: Epoch) {
        self.rent_epoch = epoch;
    }
    fn create(kelvins: u64, data: Vec<u8>, owner: Pubkey, rent_epoch: Epoch) -> Self {
        AccountSharedData {
            kelvins,
            data: Arc::new(data),
            owner,
            rent_epoch,
        }
    }
}

impl ReadableAccount for AccountSharedData {
    fn kelvins(&self) -> u64 {
        self.kelvins
    }
    fn data(&self) -> &[u8] {
        &self.data
    }
    fn owner(&self) -> &Pubkey {
        &self.owner
    }
    fn rent_epoch(&self) -> Epoch {
        self.rent_epoch
    }
}

impl ReadableAccount for Ref<'_, AccountSharedData> {
    fn kelvins(&self) -> u64 {
        self.kelvins
    }
    fn data(&self) -> &[u8] {
        &self.data
    }
    fn owner(&self) -> &Pubkey {
        &self.owner
    }
    fn rent_epoch(&self) -> Epoch {
        self.rent_epoch
    }
}

impl ReadableAccount for Ref<'_, Account> {
    fn kelvins(&self) -> u64 {
        self.kelvins
    }
    fn data(&self) -> &[u8] {
        &self.data
    }
    fn owner(&self) -> &Pubkey {
        &self.owner
    }
    fn rent_epoch(&self) -> Epoch {
        self.rent_epoch
    }
}

impl ReadableAccount for Ref<'_, StoredAccount> {
    fn kelvins(&self) -> u64 {
        StoredAccount::kelvins(self)
    }
    fn data(&self) -> &[u8] {
        StoredAccount::data(self)
    }
    fn owner(&self) -> &Pubkey {
        StoredAccount::owner(self)
    }
    fn rent_epoch(&self) -> Epoch {
        StoredAccount::rent_epoch(self)
    }
}

fn debug_fmt<T: ReadableAccount>(item: &T, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    let mut f = f.debug_struct("Account");

    f.field("kelvins", &item.kelvins())
        .field("data.len", &item.data().len())
        .field("owner", &item.owner())
        .field("rent_epoch", &item.rent_epoch());
    debug_account_data(item.data(), &mut f);

    f.finish()
}

impl fmt::Debug for Account {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        debug_fmt(self, f)
    }
}

impl fmt::Debug for AccountSharedData {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        debug_fmt(self, f)
    }
}

fn shared_new<T: WritableAccount>(kelvins: u64, space: usize, owner: &Pubkey) -> T {
    T::create(kelvins, vec![0u8; space], *owner, Epoch::default())
}

fn shared_new_rent_epoch<T: WritableAccount>(
    kelvins: u64,
    space: usize,
    owner: &Pubkey,
    rent_epoch: Epoch,
) -> T {
    T::create(kelvins, vec![0u8; space], *owner, rent_epoch)
}

fn shared_new_ref<T: WritableAccount>(
    kelvins: u64,
    space: usize,
    owner: &Pubkey,
) -> Rc<RefCell<T>> {
    Rc::new(RefCell::new(shared_new::<T>(kelvins, space, owner)))
}

#[cfg(feature = "bincode")]
fn shared_new_data<T: serde::Serialize, U: WritableAccount>(
    kelvins: u64,
    state: &T,
    owner: &Pubkey,
) -> Result<U, bincode::Error> {
    let data = bincode::serialize(state)?;
    Ok(U::create(kelvins, data, *owner, Epoch::default()))
}

#[cfg(feature = "bincode")]
fn shared_new_ref_data<T: serde::Serialize, U: WritableAccount>(
    kelvins: u64,
    state: &T,
    owner: &Pubkey,
) -> Result<RefCell<U>, bincode::Error> {
    Ok(RefCell::new(shared_new_data::<T, U>(
        kelvins, state, owner,
    )?))
}

#[cfg(feature = "bincode")]
fn shared_new_data_with_space<T: serde::Serialize, U: WritableAccount>(
    kelvins: u64,
    state: &T,
    space: usize,
    owner: &Pubkey,
) -> Result<U, bincode::Error> {
    let mut account = shared_new::<U>(kelvins, space, owner);

    shared_serialize_data(&mut account, state)?;

    Ok(account)
}

#[cfg(feature = "bincode")]
fn shared_new_ref_data_with_space<T: serde::Serialize, U: WritableAccount>(
    kelvins: u64,
    state: &T,
    space: usize,
    owner: &Pubkey,
) -> Result<RefCell<U>, bincode::Error> {
    Ok(RefCell::new(shared_new_data_with_space::<T, U>(
        kelvins, state, space, owner,
    )?))
}

#[cfg(feature = "bincode")]
fn shared_deserialize_data<T: serde::de::DeserializeOwned, U: ReadableAccount>(
    account: &U,
) -> Result<T, bincode::Error> {
    bincode::deserialize(account.data())
}

#[cfg(feature = "bincode")]
fn shared_serialize_data<T: serde::Serialize, U: WritableAccount>(
    account: &mut U,
    state: &T,
) -> Result<(), bincode::Error> {
    if bincode::serialized_size(state)? > account.data().len() as u64 {
        return Err(Box::new(bincode::ErrorKind::SizeLimit));
    }
    bincode::serialize_into(account.data_as_mut_slice(), state)
}

impl Account {
    pub fn new(kelvins: u64, space: usize, owner: &Pubkey) -> Self {
        shared_new(kelvins, space, owner)
    }
    pub fn new_ref(kelvins: u64, space: usize, owner: &Pubkey) -> Rc<RefCell<Self>> {
        shared_new_ref(kelvins, space, owner)
    }
    #[cfg(feature = "bincode")]
    pub fn new_data<T: serde::Serialize>(
        kelvins: u64,
        state: &T,
        owner: &Pubkey,
    ) -> Result<Self, bincode::Error> {
        shared_new_data(kelvins, state, owner)
    }
    #[cfg(feature = "bincode")]
    pub fn new_ref_data<T: serde::Serialize>(
        kelvins: u64,
        state: &T,
        owner: &Pubkey,
    ) -> Result<RefCell<Self>, bincode::Error> {
        shared_new_ref_data(kelvins, state, owner)
    }
    #[cfg(feature = "bincode")]
    pub fn new_data_with_space<T: serde::Serialize>(
        kelvins: u64,
        state: &T,
        space: usize,
        owner: &Pubkey,
    ) -> Result<Self, bincode::Error> {
        shared_new_data_with_space(kelvins, state, space, owner)
    }
    #[cfg(feature = "bincode")]
    pub fn new_ref_data_with_space<T: serde::Serialize>(
        kelvins: u64,
        state: &T,
        space: usize,
        owner: &Pubkey,
    ) -> Result<RefCell<Self>, bincode::Error> {
        shared_new_ref_data_with_space(kelvins, state, space, owner)
    }
    pub fn new_rent_epoch(kelvins: u64, space: usize, owner: &Pubkey, rent_epoch: Epoch) -> Self {
        shared_new_rent_epoch(kelvins, space, owner, rent_epoch)
    }
    #[cfg(feature = "bincode")]
    pub fn deserialize_data<T: serde::de::DeserializeOwned>(&self) -> Result<T, bincode::Error> {
        shared_deserialize_data(self)
    }
    #[cfg(feature = "bincode")]
    pub fn serialize_data<T: serde::Serialize>(&mut self, state: &T) -> Result<(), bincode::Error> {
        shared_serialize_data(self, state)
    }
}

impl AccountSharedData {
    pub fn is_shared(&self) -> bool {
        Arc::strong_count(&self.data) > 1
    }

    pub fn reserve(&mut self, additional: usize) {
        if let Some(data) = Arc::get_mut(&mut self.data) {
            data.reserve(additional)
        } else {
            let mut data = Vec::with_capacity(self.data.len().saturating_add(additional));
            data.extend_from_slice(&self.data);
            self.data = Arc::new(data);
        }
    }

    pub fn capacity(&self) -> usize {
        self.data.capacity()
    }

    pub fn data_clone(&self) -> Arc<Vec<u8>> {
        Arc::clone(&self.data)
    }

    fn data_mut(&mut self) -> &mut Vec<u8> {
        Arc::make_mut(&mut self.data)
    }

    pub fn resize(&mut self, new_len: usize, value: u8) {
        self.data_mut().resize(new_len, value)
    }

    pub fn extend_from_slice(&mut self, data: &[u8]) {
        self.data_mut().extend_from_slice(data)
    }

    pub fn set_data_from_slice(&mut self, new_data: &[u8]) {
        // If the buffer isn't shared, we're going to memcpy in place.
        let Some(data) = Arc::get_mut(&mut self.data) else {
            // If the buffer is shared, the cheapest thing to do is to clone the
            // incoming slice and replace the buffer.
            return self.set_data(new_data.to_vec());
        };

        let new_len = new_data.len();

        // Reserve additional capacity if needed. Here we make the assumption
        // that growing the current buffer is cheaper than doing a whole new
        // allocation to make `new_data` owned.
        //
        // This assumption holds true during CPI, especially when the account
        // size doesn't change but the account is only changed in place. And
        // it's also true when the account is grown by a small margin (the
        // realloc limit is quite low), in which case the allocator can just
        // update the allocation metadata without moving.
        //
        // Shrinking and copying in place is always faster than making
        // `new_data` owned, since shrinking boils down to updating the Vec's
        // length.

        data.reserve(new_len.saturating_sub(data.len()));

        // Safety:
        // We just reserved enough capacity. We set data::len to 0 to avoid
        // possible UB on panic (dropping uninitialized elements), do the copy,
        // finally set the new length once everything is initialized.
        #[allow(clippy::uninit_vec)]
        // this is a false positive, the lint doesn't currently special case set_len(0)
        #[allow(unsafe_code)]
        unsafe {
            data.set_len(0);
            ptr::copy_nonoverlapping(new_data.as_ptr(), data.as_mut_ptr(), new_len);
            data.set_len(new_len);
        };
    }

    #[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
    fn set_data(&mut self, data: Vec<u8>) {
        self.data = Arc::new(data);
    }

    pub fn spare_data_capacity_mut(&mut self) -> &mut [MaybeUninit<u8>] {
        self.data_mut().spare_capacity_mut()
    }

    pub fn new(kelvins: u64, space: usize, owner: &Pubkey) -> Self {
        shared_new(kelvins, space, owner)
    }
    pub fn new_ref(kelvins: u64, space: usize, owner: &Pubkey) -> Rc<RefCell<Self>> {
        shared_new_ref(kelvins, space, owner)
    }
    #[cfg(feature = "bincode")]
    pub fn new_data<T: serde::Serialize>(
        kelvins: u64,
        state: &T,
        owner: &Pubkey,
    ) -> Result<Self, bincode::Error> {
        shared_new_data(kelvins, state, owner)
    }
    #[cfg(feature = "bincode")]
    pub fn new_ref_data<T: serde::Serialize>(
        kelvins: u64,
        state: &T,
        owner: &Pubkey,
    ) -> Result<RefCell<Self>, bincode::Error> {
        shared_new_ref_data(kelvins, state, owner)
    }
    #[cfg(feature = "bincode")]
    pub fn new_data_with_space<T: serde::Serialize>(
        kelvins: u64,
        state: &T,
        space: usize,
        owner: &Pubkey,
    ) -> Result<Self, bincode::Error> {
        shared_new_data_with_space(kelvins, state, space, owner)
    }
    #[cfg(feature = "bincode")]
    pub fn new_ref_data_with_space<T: serde::Serialize>(
        kelvins: u64,
        state: &T,
        space: usize,
        owner: &Pubkey,
    ) -> Result<RefCell<Self>, bincode::Error> {
        shared_new_ref_data_with_space(kelvins, state, space, owner)
    }
    pub fn new_rent_epoch(kelvins: u64, space: usize, owner: &Pubkey, rent_epoch: Epoch) -> Self {
        shared_new_rent_epoch(kelvins, space, owner, rent_epoch)
    }
    #[cfg(feature = "bincode")]
    pub fn deserialize_data<T: serde::de::DeserializeOwned>(&self) -> Result<T, bincode::Error> {
        shared_deserialize_data(self)
    }
    #[cfg(feature = "bincode")]
    pub fn serialize_data<T: serde::Serialize>(&mut self, state: &T) -> Result<(), bincode::Error> {
        shared_serialize_data(self, state)
    }

    /// Consumes the `AccountSharedData` and returns its parts.
    ///
    /// This is useful when you need to extract the internal `Arc<Vec<u8>>` without cloning.
    pub fn into_parts(self) -> (u64, Arc<Vec<u8>>, Pubkey, Epoch) {
        (self.kelvins, self.data, self.owner, self.rent_epoch)
    }
}

pub type InheritableAccountFields = (u64, Epoch);
pub const DUMMY_INHERITABLE_ACCOUNT_FIELDS: InheritableAccountFields = (1, INITIAL_RENT_EPOCH);

#[cfg(feature = "bincode")]
pub fn create_account_with_fields<S: Sysvar>(
    sysvar: &S,
    (kelvins, rent_epoch): InheritableAccountFields,
) -> Account {
    let data_len = S::size_of().max(bincode::serialized_size(sysvar).unwrap() as usize);
    let mut account = Account::new(kelvins, data_len, &rialo_s_sdk_ids::sysvar::id());
    to_account::<S, Account>(sysvar, &mut account).unwrap();
    account.rent_epoch = rent_epoch;
    account
}

#[cfg(feature = "bincode")]
pub fn create_account_for_test<S: Sysvar>(sysvar: &S) -> Account {
    create_account_with_fields(sysvar, DUMMY_INHERITABLE_ACCOUNT_FIELDS)
}

#[cfg(feature = "bincode")]
/// Create an `Account` from a `Sysvar`.
pub fn create_account_shared_data_with_fields<S: Sysvar>(
    sysvar: &S,
    fields: InheritableAccountFields,
) -> StoredAccount {
    StoredAccount::Data(AccountSharedData::from(create_account_with_fields(
        sysvar, fields,
    )))
}

#[cfg(feature = "bincode")]
pub fn create_account_shared_data_for_test<S: Sysvar>(sysvar: &S) -> StoredAccount {
    StoredAccount::Data(AccountSharedData::from(create_account_with_fields(
        sysvar,
        DUMMY_INHERITABLE_ACCOUNT_FIELDS,
    )))
}

#[cfg(feature = "bincode")]
/// Create a `Sysvar` from an `Account`'s data.
pub fn from_account<S: Sysvar, T: ReadableAccount>(account: &T) -> Option<S> {
    bincode::deserialize(account.data()).ok()
}

#[cfg(feature = "bincode")]
/// Serialize a `Sysvar` into an `Account`'s data.
pub fn to_account<S: Sysvar, T: WritableAccount>(sysvar: &S, account: &mut T) -> Option<()> {
    bincode::serialize_into(account.data_as_mut_slice(), sysvar).ok()
}

/// Return the information required to construct an `AccountInfo`.  Used by the
/// `AccountInfo` conversion implementations.
impl rialo_s_account_info::Account for Account {
    fn get(&mut self) -> (&mut u64, &mut [u8], &Pubkey, bool, Epoch) {
        (
            &mut self.kelvins,
            &mut self.data,
            &self.owner,
            false, // Account is never executable
            self.rent_epoch,
        )
    }
}

/// Create `AccountInfo`s
pub fn create_is_signer_account_infos<'a>(
    accounts: &'a mut [(&'a Pubkey, bool, &'a mut Account)],
) -> Vec<AccountInfo<'a>> {
    accounts
        .iter_mut()
        .map(|(key, is_signer, account)| {
            AccountInfo::new(
                key,
                *is_signer,
                false,
                &mut account.kelvins,
                &mut account.data,
                &account.owner,
                false, // Account is never executable
                account.rent_epoch,
            )
        })
        .collect()
}

/// Replacement for the executable flag: An account being owned by one of these contains a program.
/// This is the single source of truth for all program loader owners.
pub const PROGRAM_OWNERS: &[Pubkey] = &[
    bpf_loader_upgradeable::id(),
    bpf_loader::id(),
    bpf_loader_deprecated::id(),
    loader_v4::id(),
    risc_v_loader::id(),
    rex_wasm_v1_loader::id(),
    native_loader::id(),
];

#[cfg(test)]
pub mod tests {
    use super::*;

    fn make_two_accounts(key: &Pubkey) -> (Account, AccountSharedData) {
        let mut account1 = Account::new(1, 2, key);
        account1.rent_epoch = 4;
        let mut account2 = AccountSharedData::new(1, 2, key);
        account2.rent_epoch = 4;
        assert!(accounts_equal(&account1, &account2));
        (account1, account2)
    }

    #[test]
    fn test_account_data_copy_as_slice() {
        let key = Pubkey::new_unique();
        let key2 = Pubkey::new_unique();
        let (mut account1, mut account2) = make_two_accounts(&key);
        account1.copy_into_owner_from_slice(key2.as_ref());
        account2.copy_into_owner_from_slice(key2.as_ref());
        assert!(accounts_equal(&account1, &account2));
        assert_eq!(account1.owner(), &key2);
    }

    #[test]
    fn test_account_set_data_from_slice() {
        let key = Pubkey::new_unique();
        let (_, mut account) = make_two_accounts(&key);
        assert_eq!(account.data(), &vec![0, 0]);
        account.set_data_from_slice(&[1, 2]);
        assert_eq!(account.data(), &vec![1, 2]);
        account.set_data_from_slice(&[1, 2, 3]);
        assert_eq!(account.data(), &vec![1, 2, 3]);
        account.set_data_from_slice(&[4, 5, 6]);
        assert_eq!(account.data(), &vec![4, 5, 6]);
        account.set_data_from_slice(&[4, 5, 6, 0]);
        assert_eq!(account.data(), &vec![4, 5, 6, 0]);
        account.set_data_from_slice(&[]);
        assert_eq!(account.data().len(), 0);
        account.set_data_from_slice(&[44]);
        assert_eq!(account.data(), &vec![44]);
        account.set_data_from_slice(&[44]);
        assert_eq!(account.data(), &vec![44]);
    }

    #[test]
    fn test_account_data_set_data() {
        let key = Pubkey::new_unique();
        let (_, mut account) = make_two_accounts(&key);
        assert_eq!(account.data(), &vec![0, 0]);
        account.set_data(vec![1, 2]);
        assert_eq!(account.data(), &vec![1, 2]);
        account.set_data(vec![]);
        assert_eq!(account.data().len(), 0);
    }

    #[test]
    #[should_panic(
        expected = "called `Result::unwrap()` on an `Err` value: Io(Kind(UnexpectedEof))"
    )]
    fn test_account_deserialize() {
        let key = Pubkey::new_unique();
        let (account1, _account2) = make_two_accounts(&key);
        account1.deserialize_data::<String>().unwrap();
    }

    #[test]
    #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: SizeLimit")]
    fn test_account_serialize() {
        let key = Pubkey::new_unique();
        let (mut account1, _account2) = make_two_accounts(&key);
        account1.serialize_data(&"hello world").unwrap();
    }

    #[test]
    #[should_panic(
        expected = "called `Result::unwrap()` on an `Err` value: Io(Kind(UnexpectedEof))"
    )]
    fn test_account_shared_data_deserialize() {
        let key = Pubkey::new_unique();
        let (_account1, account2) = make_two_accounts(&key);
        account2.deserialize_data::<String>().unwrap();
    }

    #[test]
    #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: SizeLimit")]
    fn test_account_shared_data_serialize() {
        let key = Pubkey::new_unique();
        let (_account1, mut account2) = make_two_accounts(&key);
        account2.serialize_data(&"hello world").unwrap();
    }

    #[test]
    fn test_account_shared_data() {
        let key = Pubkey::new_unique();
        let (account1, account2) = make_two_accounts(&key);
        assert!(accounts_equal(&account1, &account2));
        let account = account1;
        assert_eq!(account.kelvins, 1);
        assert_eq!(account.kelvins(), 1);
        assert_eq!(account.data.len(), 2);
        assert_eq!(account.data().len(), 2);
        assert_eq!(account.owner, key);
        assert_eq!(account.owner(), &key);
        // Note: executable status is tracked by StoredAccount variant, not Account/AccountSharedData
        assert_eq!(account.rent_epoch, 4);
        assert_eq!(account.rent_epoch(), 4);
        let account = account2;
        assert_eq!(account.kelvins, 1);
        assert_eq!(account.kelvins(), 1);
        assert_eq!(account.data.len(), 2);
        assert_eq!(account.data().len(), 2);
        assert_eq!(account.owner, key);
        assert_eq!(account.owner(), &key);
        // Note: executable status is tracked by StoredAccount variant, not Account/AccountSharedData
        assert_eq!(account.rent_epoch, 4);
        assert_eq!(account.rent_epoch(), 4);
    }

    // test clone and from for both types against expected
    fn test_equal(
        should_be_equal: bool,
        account1: &Account,
        account2: &AccountSharedData,
        account_expected: &Account,
    ) {
        assert_eq!(should_be_equal, accounts_equal(account1, account2));
        if should_be_equal {
            assert!(accounts_equal(account_expected, account2));
        }
        assert_eq!(
            accounts_equal(account_expected, account1),
            accounts_equal(account_expected, &account1.clone())
        );
        assert_eq!(
            accounts_equal(account_expected, account2),
            accounts_equal(account_expected, &account2.clone())
        );
        assert_eq!(
            accounts_equal(account_expected, account1),
            accounts_equal(account_expected, &AccountSharedData::from(account1.clone()))
        );
        assert_eq!(
            accounts_equal(account_expected, account2),
            accounts_equal(account_expected, &Account::from(account2.clone()))
        );
    }

    #[test]
    fn test_account_add_sub_kelvins() {
        let key = Pubkey::new_unique();
        let (mut account1, mut account2) = make_two_accounts(&key);
        assert!(accounts_equal(&account1, &account2));
        account1.checked_add_kelvins(1).unwrap();
        account2.checked_add_kelvins(1).unwrap();
        assert!(accounts_equal(&account1, &account2));
        assert_eq!(account1.kelvins(), 2);
        account1.checked_sub_kelvins(2).unwrap();
        account2.checked_sub_kelvins(2).unwrap();
        assert!(accounts_equal(&account1, &account2));
        assert_eq!(account1.kelvins(), 0);
    }

    #[test]
    #[should_panic(expected = "Overflow")]
    fn test_account_checked_add_kelvins_overflow() {
        let key = Pubkey::new_unique();
        let (mut account1, _account2) = make_two_accounts(&key);
        account1.checked_add_kelvins(u64::MAX).unwrap();
    }

    #[test]
    #[should_panic(expected = "Underflow")]
    fn test_account_checked_sub_kelvins_underflow() {
        let key = Pubkey::new_unique();
        let (mut account1, _account2) = make_two_accounts(&key);
        account1.checked_sub_kelvins(u64::MAX).unwrap();
    }

    #[test]
    #[should_panic(expected = "Overflow")]
    fn test_account_checked_add_kelvins_overflow2() {
        let key = Pubkey::new_unique();
        let (_account1, mut account2) = make_two_accounts(&key);
        account2.checked_add_kelvins(u64::MAX).unwrap();
    }

    #[test]
    #[should_panic(expected = "Underflow")]
    fn test_account_checked_sub_kelvins_underflow2() {
        let key = Pubkey::new_unique();
        let (_account1, mut account2) = make_two_accounts(&key);
        account2.checked_sub_kelvins(u64::MAX).unwrap();
    }

    #[test]
    fn test_account_saturating_add_kelvins() {
        let key = Pubkey::new_unique();
        let (mut account, _) = make_two_accounts(&key);

        let remaining = 22;
        account.set_kelvins(u64::MAX - remaining);
        account.saturating_add_kelvins(remaining * 2);
        assert_eq!(account.kelvins(), u64::MAX);
    }

    #[test]
    fn test_account_saturating_sub_kelvins() {
        let key = Pubkey::new_unique();
        let (mut account, _) = make_two_accounts(&key);

        let remaining = 33;
        account.set_kelvins(remaining);
        account.saturating_sub_kelvins(remaining * 2);
        assert_eq!(account.kelvins(), 0);
    }

    #[test]
    fn test_account_shared_data_all_fields() {
        let key = Pubkey::new_unique();
        let key2 = Pubkey::new_unique();
        let key3 = Pubkey::new_unique();
        let (mut account1, mut account2) = make_two_accounts(&key);
        assert!(accounts_equal(&account1, &account2));

        let mut account_expected = account1.clone();
        assert!(accounts_equal(&account1, &account_expected));
        assert!(accounts_equal(&account1, &account2.clone())); // test the clone here

        for field_index in 0..4 {
            for pass in 0..4 {
                if field_index == 0 {
                    if pass == 0 {
                        account1.checked_add_kelvins(1).unwrap();
                    } else if pass == 1 {
                        account_expected.checked_add_kelvins(1).unwrap();
                        account2.set_kelvins(account2.kelvins + 1);
                    } else if pass == 2 {
                        account1.set_kelvins(account1.kelvins + 1);
                    } else if pass == 3 {
                        account_expected.checked_add_kelvins(1).unwrap();
                        account2.checked_add_kelvins(1).unwrap();
                    }
                } else if field_index == 1 {
                    if pass == 0 {
                        account1.data[0] += 1;
                    } else if pass == 1 {
                        account_expected.data[0] += 1;
                        account2.data_as_mut_slice()[0] = account2.data[0] + 1;
                    } else if pass == 2 {
                        account1.data_as_mut_slice()[0] = account1.data[0] + 1;
                    } else if pass == 3 {
                        account_expected.data[0] += 1;
                        account2.data_as_mut_slice()[0] += 1;
                    }
                } else if field_index == 2 {
                    if pass == 0 {
                        account1.owner = key2;
                    } else if pass == 1 {
                        account_expected.owner = key2;
                        account2.set_owner(key2);
                    } else if pass == 2 {
                        account1.set_owner(key3);
                    } else if pass == 3 {
                        account_expected.owner = key3;
                        account2.owner = key3;
                    }
                } else if field_index == 3 {
                    if pass == 0 {
                        account1.rent_epoch += 1;
                    } else if pass == 1 {
                        account_expected.rent_epoch += 1;
                        account2.set_rent_epoch(account2.rent_epoch + 1);
                    } else if pass == 2 {
                        account1.set_rent_epoch(account1.rent_epoch + 1);
                    } else if pass == 3 {
                        account_expected.rent_epoch += 1;
                        account2.rent_epoch += 1;
                    }
                }

                let should_be_equal = pass == 1 || pass == 3;
                test_equal(should_be_equal, &account1, &account2, &account_expected);

                // test new_ref
                if should_be_equal {
                    assert!(accounts_equal(
                        &Account::new_ref(
                            account_expected.kelvins(),
                            account_expected.data().len(),
                            account_expected.owner()
                        )
                        .borrow(),
                        &AccountSharedData::new_ref(
                            account_expected.kelvins(),
                            account_expected.data().len(),
                            account_expected.owner()
                        )
                        .borrow()
                    ));

                    {
                        // test new_data
                        let account1_with_data = Account::new_data(
                            account_expected.kelvins(),
                            &account_expected.data()[0],
                            account_expected.owner(),
                        )
                        .unwrap();
                        let account2_with_data = AccountSharedData::new_data(
                            account_expected.kelvins(),
                            &account_expected.data()[0],
                            account_expected.owner(),
                        )
                        .unwrap();

                        assert!(accounts_equal(&account1_with_data, &account2_with_data));
                        assert_eq!(
                            account1_with_data.deserialize_data::<u8>().unwrap(),
                            account2_with_data.deserialize_data::<u8>().unwrap()
                        );
                    }

                    // test new_data_with_space
                    assert!(accounts_equal(
                        &Account::new_data_with_space(
                            account_expected.kelvins(),
                            &account_expected.data()[0],
                            1,
                            account_expected.owner()
                        )
                        .unwrap(),
                        &AccountSharedData::new_data_with_space(
                            account_expected.kelvins(),
                            &account_expected.data()[0],
                            1,
                            account_expected.owner()
                        )
                        .unwrap()
                    ));

                    // test new_ref_data
                    assert!(accounts_equal(
                        &Account::new_ref_data(
                            account_expected.kelvins(),
                            &account_expected.data()[0],
                            account_expected.owner()
                        )
                        .unwrap()
                        .borrow(),
                        &AccountSharedData::new_ref_data(
                            account_expected.kelvins(),
                            &account_expected.data()[0],
                            account_expected.owner()
                        )
                        .unwrap()
                        .borrow()
                    ));

                    //new_ref_data_with_space
                    assert!(accounts_equal(
                        &Account::new_ref_data_with_space(
                            account_expected.kelvins(),
                            &account_expected.data()[0],
                            1,
                            account_expected.owner()
                        )
                        .unwrap()
                        .borrow(),
                        &AccountSharedData::new_ref_data_with_space(
                            account_expected.kelvins(),
                            &account_expected.data()[0],
                            1,
                            account_expected.owner()
                        )
                        .unwrap()
                        .borrow()
                    ));
                }
            }
        }
    }

    // ============== BalanceAccount Tests ==============

    #[test]
    fn test_balance_account_new() {
        let owner = Pubkey::new_unique();
        let balance = BalanceAccount::new(100, &owner);

        assert_eq!(balance.kelvins(), 100);
        assert_eq!(balance.owner(), &owner);
        assert_eq!(balance.rent_epoch(), 0);
        assert!(balance.data().is_empty());
        // BalanceAccount is never executable (by design)
    }

    #[test]
    fn test_balance_account_new_with_rent_epoch() {
        let owner = Pubkey::new_unique();
        let balance = BalanceAccount::new_with_rent_epoch(200, &owner, 42);

        assert_eq!(balance.kelvins(), 200);
        assert_eq!(balance.owner(), &owner);
        assert_eq!(balance.rent_epoch(), 42);
        assert!(balance.data().is_empty());
    }

    #[test]
    fn test_balance_account_from_account_shared_data() {
        let owner = Pubkey::new_unique();
        let mut account = AccountSharedData::new(500, 10, &owner);
        account.set_rent_epoch(99);

        let balance = BalanceAccount::from(&account);

        assert_eq!(balance.kelvins(), 500);
        assert_eq!(balance.owner(), &owner);
        assert_eq!(balance.rent_epoch(), 99);
        assert!(balance.data().is_empty());
    }

    #[test]
    fn test_balance_account_from_account() {
        let owner = Pubkey::new_unique();
        let mut account = Account::new(300, 5, &owner);
        account.rent_epoch = 77;

        let balance = BalanceAccount::from(&account);

        assert_eq!(balance.kelvins(), 300);
        assert_eq!(balance.owner(), &owner);
        assert_eq!(balance.rent_epoch(), 77);
        assert!(balance.data().is_empty());
    }

    #[test]
    fn test_balance_account_default() {
        let balance = BalanceAccount::default();

        assert_eq!(balance.kelvins(), 0);
        assert_eq!(balance.owner(), &Pubkey::default());
        assert_eq!(balance.rent_epoch(), 0);
    }

    // ============== StoredAccount Tests ==============

    #[test]
    fn test_stored_account_data_variant() {
        let owner = Pubkey::new_unique();
        let account = AccountSharedData::new(1000, 50, &owner);
        let stored = StoredAccount::from(account.clone());

        assert!(stored.is_data());
        assert!(!stored.is_balance());
        assert_eq!(stored.kelvins(), 1000);
        assert_eq!(stored.owner(), &owner);
        assert_eq!(stored.data().len(), 50);
        assert!(!stored.executable());
    }

    #[test]
    fn test_stored_account_balance_variant() {
        let owner = Pubkey::new_unique();
        let balance = BalanceAccount::new(2000, &owner);
        let stored = StoredAccount::from(balance);

        assert!(!stored.is_data());
        assert!(stored.is_balance());
        assert_eq!(stored.kelvins(), 2000);
        assert_eq!(stored.owner(), &owner);
        assert!(stored.data().is_empty());
        assert!(!stored.executable());
    }

    #[test]
    fn test_stored_account_as_data() {
        let owner = Pubkey::new_unique();
        let account = AccountSharedData::new(100, 10, &owner);
        let stored_data = StoredAccount::from(account.clone());
        let stored_balance = StoredAccount::from(BalanceAccount::new(100, &owner));

        assert!(stored_data.as_data().is_some());
        assert!(stored_data.as_balance().is_none());
        assert!(stored_balance.as_data().is_none());
        assert!(stored_balance.as_balance().is_some());
    }

    #[test]
    fn test_stored_account_default() {
        let stored = StoredAccount::default();

        assert!(stored.is_data());
        assert_eq!(stored.kelvins(), 0);
    }

    #[test]
    fn test_stored_account_from_account() {
        let owner = Pubkey::new_unique();
        let account = Account::new(500, 20, &owner);
        let stored = StoredAccount::from(account);

        assert!(stored.is_data());
        assert_eq!(stored.kelvins(), 500);
        assert_eq!(stored.data().len(), 20);
    }

    #[test]
    fn test_account_shared_data_from_stored_account() {
        let owner = Pubkey::new_unique();

        // Test Data variant
        let account = AccountSharedData::new(1000, 30, &owner);
        let stored = StoredAccount::from(account.clone());
        let converted: AccountSharedData = stored.into();
        assert!(accounts_equal(&account, &converted));

        // Test Balance variant
        let balance = BalanceAccount::new_with_rent_epoch(2000, &owner, 789);
        let stored = StoredAccount::from(balance);
        let converted: AccountSharedData = stored.into();
        assert_eq!(converted.kelvins(), 2000);
        assert_eq!(converted.owner(), &owner);
        assert_eq!(converted.rent_epoch(), 789);
        assert!(converted.data().is_empty());
    }

    #[test]
    fn test_stored_account_readable_account_trait() {
        let owner = Pubkey::new_unique();

        // Test with Data variant
        let mut account = AccountSharedData::new(100, 5, &owner);
        account.set_rent_epoch(10);
        let stored_data = StoredAccount::Data(account.clone());

        assert_eq!(stored_data.kelvins(), account.kelvins());
        assert_eq!(stored_data.data(), account.data());
        assert_eq!(stored_data.owner(), account.owner());
        assert!(!stored_data.executable()); // Data variant is not executable
        assert_eq!(stored_data.rent_epoch(), account.rent_epoch());

        // Test with Balance variant
        let balance = BalanceAccount::new_with_rent_epoch(200, &owner, 20);
        let stored_balance = StoredAccount::Balance(balance.clone());

        assert_eq!(stored_balance.kelvins(), balance.kelvins());
        assert_eq!(stored_balance.data(), balance.data());
        assert_eq!(stored_balance.owner(), balance.owner());
        assert!(!stored_balance.executable()); // Balance variant is not executable
        assert_eq!(stored_balance.rent_epoch(), balance.rent_epoch());
    }

    // ============== ExecutableAccount Helper Method Tests ==============

    #[test]
    fn test_executable_account_bytecode_format() {
        // Test RexWasmV1Loader accounts return Wasm
        let rex_owner = rex_wasm_v1_loader::id();
        let rex_account = ExecutableAccount::new(100, vec![0; 10], &rex_owner, 0);
        assert_eq!(rex_account.bytecode_format(), BytecodeFormat::Wasm);

        // Test legacy loader accounts return PolkaVm
        let legacy_owner = risc_v_loader::id();
        let legacy_account = ExecutableAccount::new(100, vec![0; 10], &legacy_owner, 0);
        assert_eq!(legacy_account.bytecode_format(), BytecodeFormat::PolkaVm);

        // Test another legacy loader
        let loader_v4_owner = loader_v4::id();
        let loader_v4_account = ExecutableAccount::new(100, vec![0; 10], &loader_v4_owner, 0);
        assert_eq!(loader_v4_account.bytecode_format(), BytecodeFormat::PolkaVm);
    }

    #[test]
    fn test_executable_account_execution_context() {
        // Test RexWasmV1Loader accounts return Rex
        let rex_owner = rex_wasm_v1_loader::id();
        let rex_account = ExecutableAccount::new(100, vec![0; 10], &rex_owner, 0);
        assert_eq!(rex_account.execution_context(), ExecutionContext::Rex);

        // Test legacy loader accounts return Chain
        let legacy_owner = risc_v_loader::id();
        let legacy_account = ExecutableAccount::new(100, vec![0; 10], &legacy_owner, 0);
        assert_eq!(legacy_account.execution_context(), ExecutionContext::Chain);

        // Test another legacy loader
        let loader_v4_owner = loader_v4::id();
        let loader_v4_account = ExecutableAccount::new(100, vec![0; 10], &loader_v4_owner, 0);
        assert_eq!(
            loader_v4_account.execution_context(),
            ExecutionContext::Chain
        );
    }

    #[test]
    fn test_executable_account_helper_methods_consistency() {
        // Test that format and context match for RexWasmV1Loader
        let rex_owner = rex_wasm_v1_loader::id();
        let rex_account = ExecutableAccount::new(500, vec![1, 2, 3], &rex_owner, 42);

        assert_eq!(rex_account.bytecode_format(), BytecodeFormat::Wasm);
        assert_eq!(rex_account.execution_context(), ExecutionContext::Rex);
        assert_eq!(rex_account.kelvins(), 500);
        assert_eq!(rex_account.data().len(), 3);
        assert_eq!(rex_account.rent_epoch(), 42);

        // Test that format and context match for legacy loaders
        let legacy_owner = bpf_loader::id();
        let legacy_account = ExecutableAccount::new(1000, vec![4, 5, 6, 7], &legacy_owner, 100);

        assert_eq!(legacy_account.bytecode_format(), BytecodeFormat::PolkaVm);
        assert_eq!(legacy_account.execution_context(), ExecutionContext::Chain);
        assert_eq!(legacy_account.kelvins(), 1000);
        assert_eq!(legacy_account.data().len(), 4);
        assert_eq!(legacy_account.rent_epoch(), 100);
    }
}
