use solana_instruction::TRANSACTION_LEVEL_STACK_HEIGHT;
use solana_message::{
    compiled_instruction::CompiledInstruction,
    inner_instruction::{InnerInstruction, InnerInstructionsList},
};
use solana_transaction_context::TransactionContext;

/// Pulled verbatim from `solana-svm` crate, `transaction_processor.rs`
pub(crate) fn inner_instructions_list_from_instruction_trace(
    transaction_context: &TransactionContext<'_>,
) -> InnerInstructionsList {
    debug_assert!(transaction_context.get_instruction_context_at_index_in_trace(0).map_or(
        true,
        |instruction_context| instruction_context.get_stack_height() ==
            TRANSACTION_LEVEL_STACK_HEIGHT
    ));
    let mut outer_instructions = Vec::new();
    for index_in_trace in 0..transaction_context.get_instruction_trace_length() {
        if let Ok(instruction_context) =
            transaction_context.get_instruction_context_at_index_in_trace(index_in_trace)
        {
            let stack_height = instruction_context.get_stack_height();
            if stack_height == TRANSACTION_LEVEL_STACK_HEIGHT {
                outer_instructions.push(Vec::new());
            } else if let Some(inner_instructions) = outer_instructions.last_mut() {
                let stack_height = u8::try_from(stack_height).unwrap_or(u8::MAX);
                let instruction = CompiledInstruction::new_from_raw_parts(
                    instruction_context
                        .get_index_of_program_account_in_transaction()
                        .unwrap_or_default() as u8,
                    instruction_context.get_instruction_data().to_vec(),
                    (0..instruction_context.get_number_of_instruction_accounts())
                        .map(|instruction_account_index| {
                            instruction_context
                                .get_index_of_instruction_account_in_transaction(
                                    instruction_account_index,
                                )
                                .unwrap_or_default() as u8
                        })
                        .collect(),
                );
                inner_instructions.push(InnerInstruction { instruction, stack_height });
            } else {
                debug_assert!(false);
            }
        } else {
            debug_assert!(false);
        }
    }
    outer_instructions
}
