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

use std::str::FromStr;

use crate::rpc::types::Pubkey;

/// Represents a blockchain instruction to be executed by a specific program.
///
/// An instruction contains:
/// - The program ID that will process this instruction
/// - A list of accounts that will be accessed during execution
/// - Instruction-specific data encoded as bytes
///
/// # Examples
///
/// ```
/// use rialo_cdk::transaction::instruction::{Instruction, AccountMeta};
/// use rialo_cdk::rpc::types::Pubkey;
/// use std::str::FromStr;
///
/// let program_id = Pubkey::from_str("11111111111111111111111111111111").unwrap();
/// let instruction = Instruction {
///     program_id,
///     accounts: vec![/* account metadata */],
///     data: vec![/* encoded instruction data */],
/// };
/// ```
#[derive(Debug, Clone)]
pub struct Instruction {
    /// The program ID (as a base58-encoded string) that will execute this instruction
    pub program_id: Pubkey,
    /// The accounts required for this instruction to execute
    pub accounts: Vec<AccountMeta>,
    /// Program-specific instruction data as bytes
    pub data: Vec<u8>,
}

/// Metadata for an account that will be accessed by an instruction.
///
/// This structure defines how an account will be used within an instruction:
/// - Which account (via its public key)
/// - Whether it's required to sign the transaction
/// - Whether the program will write to this account
///
/// # Examples
///
/// ```
/// use rialo_cdk::transaction::instruction::AccountMeta;
/// use rialo_cdk::rpc::types::Pubkey;
/// use std::str::FromStr;
///
/// let pubkey = Pubkey::from_str("11111111111111111111111111111111").unwrap();
/// let account_meta = AccountMeta {
///     pubkey,
///     is_signer: true,
///     is_writable: false,
/// };
/// ```
#[derive(Debug, Clone)]
pub struct AccountMeta {
    /// The account's public key as a base58-encoded string
    pub pubkey: Pubkey,
    /// Whether this account needs to sign the transaction
    pub is_signer: bool,
    /// Whether this account's data will be modified during instruction execution
    pub is_writable: bool,
}

/// Creates a transfer instruction for the System Program.
///
/// This function builds an instruction that transfers `kelvin` from one account to another
/// using the System Program. Both the sender and receiver accounts are specified by their
/// base58-encoded public keys.
///
/// # Arguments
///
/// * `from` - Base58-encoded public key of the sender account (must be a signer)
/// * `to` - Base58-encoded public key of the recipient account
/// * `kelvin` - Amount of kelvin (the smallest unit of currency) to transfer
///
/// # Returns
///
/// An `Instruction` that can be included in a transaction to perform the transfer
///
/// # Examples
///
/// ```
/// use rialo_cdk::transaction::instruction::transfer_instruction;
/// use rialo_cdk::rpc::types::Pubkey;
/// use std::str::FromStr;
///
/// let from_address = Pubkey::from_str("11111111111111111111111111111112").unwrap();
/// let to_address = Pubkey::from_str("11111111111111111111111111111113").unwrap();
/// let amount = 1_000_000; // kelvin
///
/// let transfer_ix = transfer_instruction(&from_address, &to_address, amount);
/// // Include this instruction in a transaction to execute the transfer
/// ```
pub fn transfer_instruction(from: &Pubkey, to: &Pubkey, kelvin: u64) -> Instruction {
    // The System Program ID is a well-known address in base58
    let program_id = Pubkey::from_str("11111111111111111111111111111111").unwrap();

    // Create account metadata for the transfer
    // Note: from and to are already expected to be base58-encoded strings
    let accounts = vec![
        // From account is a signer and writable (funds will be deducted)
        AccountMeta {
            pubkey: *from,
            is_signer: true,
            is_writable: true,
        },
        // To account is writable (funds will be added) but not a signer
        AccountMeta {
            pubkey: *to,
            is_signer: false,
            is_writable: true,
        },
    ];

    // Encode the transfer instruction data
    // For the system program, transfer is instruction index 2
    // The data format is: [instruction index as 4-byte little-endian u32, kelvin as little-endian bytes]
    let mut data = vec![2, 0, 0, 0]; // Instruction index 2 (transfer) as 4-byte little-endian u32
    data.extend_from_slice(&kelvin.to_le_bytes()); // Append amount in little-endian format

    Instruction {
        program_id,
        accounts,
        data,
    }
}
