use solana_program::instruction::AccountMeta;
use solana_program::pubkey::Pubkey;

/// Parameters for building Kamino remaining accounts for `refreshObligation`.
///
/// Klend expects remaining accounts in order: deposit reserves, then borrow reserves.
/// Each reserve account is writable.
pub struct KaminoRemainingAccountsParams {
    /// Reserve pubkeys for each deposited asset, in obligation deposit order.
    pub deposit_reserves: Vec<Pubkey>,
    /// Reserve pubkeys for each borrowed asset, in obligation borrow order.
    pub borrow_reserves: Vec<Pubkey>,
}

/// Builds the remaining accounts for Klend's `refreshObligation` instruction.
///
/// Returns accounts in the order Klend expects:
/// 1. Deposit reserve accounts (writable)
/// 2. Borrow reserve accounts (writable)
pub fn get_kamino_remaining_accounts(
    params: &KaminoRemainingAccountsParams,
) -> Vec<AccountMeta> {
    let mut accounts = Vec::with_capacity(
        params.deposit_reserves.len().saturating_add(params.borrow_reserves.len()),
    );

    for reserve in &params.deposit_reserves {
        accounts.push(AccountMeta::new(*reserve, false));
    }

    for reserve in &params.borrow_reserves {
        accounts.push(AccountMeta::new(*reserve, false));
    }

    accounts
}

#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
    use super::*;
    use solana_pubkey::pubkey;

    #[test]
    fn test_deposit_and_borrow_reserves() {
        let deposit_a = pubkey!("d4A2prbA2whesmvHaL88BH6Ewn5N4bTSjm4GiKy2eSi");
        let deposit_b = pubkey!("7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF");
        let borrow_a = pubkey!("Gnt27xtC473ZT2Mw5u8wZ68Z3gULkSTb5DuxJy7eJotD");

        let accounts = get_kamino_remaining_accounts(&KaminoRemainingAccountsParams {
            deposit_reserves: vec![deposit_a, deposit_b],
            borrow_reserves: vec![borrow_a],
        });

        assert_eq!(accounts.len(), 3);
        // Deposits first
        assert_eq!(accounts[0].pubkey, deposit_a);
        assert!(accounts[0].is_writable);
        assert_eq!(accounts[1].pubkey, deposit_b);
        assert!(accounts[1].is_writable);
        // Then borrows
        assert_eq!(accounts[2].pubkey, borrow_a);
        assert!(accounts[2].is_writable);
        // None are signers
        for acc in &accounts {
            assert!(!acc.is_signer);
        }
    }

    #[test]
    fn test_empty_params() {
        let accounts = get_kamino_remaining_accounts(&KaminoRemainingAccountsParams {
            deposit_reserves: vec![],
            borrow_reserves: vec![],
        });

        assert!(accounts.is_empty());
    }

    #[test]
    fn test_deposits_only() {
        let deposit = pubkey!("d4A2prbA2whesmvHaL88BH6Ewn5N4bTSjm4GiKy2eSi");

        let accounts = get_kamino_remaining_accounts(&KaminoRemainingAccountsParams {
            deposit_reserves: vec![deposit],
            borrow_reserves: vec![],
        });

        assert_eq!(accounts.len(), 1);
        assert_eq!(accounts[0].pubkey, deposit);
        assert!(accounts[0].is_writable);
    }

    #[test]
    fn test_borrows_only() {
        let borrow = pubkey!("Gnt27xtC473ZT2Mw5u8wZ68Z3gULkSTb5DuxJy7eJotD");

        let accounts = get_kamino_remaining_accounts(&KaminoRemainingAccountsParams {
            deposit_reserves: vec![],
            borrow_reserves: vec![borrow],
        });

        assert_eq!(accounts.len(), 1);
        assert_eq!(accounts[0].pubkey, borrow);
        assert!(accounts[0].is_writable);
    }
}
