use {
    crate::{
        account_rent_state::{check_rent_state, RentState},
        bank::Bank,
    },
    mundis_program_runtime::invoke_context::TransactionAccountRefCell,
    mundis_sdk::{
        account::ReadableAccount, feature_set, message::SanitizedMessage, native_loader,
        transaction::Result,
    },
};

pub(crate) struct TransactionAccountStateInfo {
    rent_state: Option<RentState>, // None: readonly account
}

impl Bank {
    pub(crate) fn get_transaction_account_state_info(
        &self,
        transaction_account_refcells: &[TransactionAccountRefCell],
        message: &SanitizedMessage,
    ) -> Vec<TransactionAccountStateInfo> {
        transaction_account_refcells
            .iter()
            .take(message.account_keys_len())
            .enumerate()
            .map(|(i, (_pubkey, account_refcell))| {
                let account = account_refcell.borrow();

                let rent_state = if message.is_writable(i) {
                    // Native programs appear to be RentPaying because they carry low lamport
                    // balances; however they will never be loaded as writable
                    debug_assert!(!native_loader::check_id(account.owner()));

                    Some(RentState::from_account(
                        &account,
                        &self.rent_collector().rent,
                    ))
                } else {
                    None
                };
                TransactionAccountStateInfo { rent_state }
            })
            .collect()
    }

    pub(crate) fn verify_transaction_account_state_changes(
        &self,
        pre_state_infos: &[TransactionAccountStateInfo],
        post_state_infos: &[TransactionAccountStateInfo],
        transaction_account_refcells: &[TransactionAccountRefCell],
    ) -> Result<()> {
        let require_rent_exempt_accounts = self
            .feature_set
            .is_active(&feature_set::require_rent_exempt_accounts::id());
        let do_support_realloc = self
            .feature_set
            .is_active(&feature_set::do_support_realloc::id());
        for ((pre_state_info, post_state_info), (pubkey, account_refcell)) in pre_state_infos
            .iter()
            .zip(post_state_infos)
            .zip(transaction_account_refcells)
        {
            if let Err(err) = check_rent_state(
                pre_state_info.rent_state.as_ref(),
                post_state_info.rent_state.as_ref(),
                pubkey,
                &account_refcell.borrow(),
                do_support_realloc,
            ) {
                // Feature gate only wraps the actual error return so that the metrics and debug
                // logging generated by `check_rent_state()` can be examined before feature
                // activation
                if require_rent_exempt_accounts {
                    return Err(err);
                }
            }
        }
        Ok(())
    }
}
