use anchor_lang::{InstructionData, ToAccountMetas};
use solana_program::{
    instruction::{AccountMeta, Instruction},
    pubkey::Pubkey,
};

use crate::constants::KAMINO_LENDING_PROGRAM_ID;

pub struct RefreshKaminoObligationParams {
    pub obligation: Pubkey,
    pub lending_market: Pubkey,
    /// Remaining accounts: deposit reserves followed by borrow reserves.
    /// Use `get_kamino_remaining_accounts` to build this list.
    pub remaining_accounts: Vec<AccountMeta>,
}

pub fn refresh_kamino_obligation(params: &RefreshKaminoObligationParams) -> Instruction {
    let mut accounts = crate::kamino_lending_program::client::accounts::RefreshObligation {
        lending_market: params.lending_market,
        obligation: params.obligation,
    }
    .to_account_metas(None);
    accounts.extend_from_slice(&params.remaining_accounts);

    let data = crate::kamino_lending_program::client::args::RefreshObligation {}.data();

    Instruction {
        program_id: KAMINO_LENDING_PROGRAM_ID,
        accounts,
        data,
    }
}

#[cfg(test)]
#[allow(clippy::allow_attributes, clippy::allow_attributes_without_reason, clippy::unwrap_used, reason = "test code")]
mod tests {
    use super::*;
    use solana_pubkey::pubkey;

    #[test]
    fn test_refresh_obligation_instruction() {
        let obligation = pubkey!("d4A2prbA2whesmvHaL88BH6Ewn5N4bTSjm4GiKy2eSi");
        let lending_market = pubkey!("7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF");
        let reserve_a = pubkey!("Gnt27xtC473ZT2Mw5u8wZ68Z3gULkSTb5DuxJy7eJotD");
        let reserve_b = pubkey!("AdtRGGhmqvom3Fk5H2LTnGMfcXmUQdhSz8aPJGfhFqHj");

        let remaining = vec![
            AccountMeta::new(reserve_a, false),
            AccountMeta::new(reserve_b, false),
        ];

        let ix = refresh_kamino_obligation(&RefreshKaminoObligationParams {
            obligation,
            lending_market,
            remaining_accounts: remaining,
        });

        assert_eq!(ix.program_id, KAMINO_LENDING_PROGRAM_ID);
        // 2 named accounts + 2 remaining
        assert_eq!(ix.accounts.len(), 4);
        // lending_market is read-only
        assert!(!ix.accounts[0].is_writable);
        assert_eq!(ix.accounts[0].pubkey, lending_market);
        // obligation is writable
        assert!(ix.accounts[1].is_writable);
        assert_eq!(ix.accounts[1].pubkey, obligation);
        // remaining accounts preserved
        assert_eq!(ix.accounts[2].pubkey, reserve_a);
        assert_eq!(ix.accounts[3].pubkey, reserve_b);
        // data should not be empty
        assert!(!ix.data.is_empty());
    }

    #[test]
    fn test_refresh_obligation_no_remaining() {
        let obligation = pubkey!("d4A2prbA2whesmvHaL88BH6Ewn5N4bTSjm4GiKy2eSi");
        let lending_market = pubkey!("7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF");

        let ix = refresh_kamino_obligation(&RefreshKaminoObligationParams {
            obligation,
            lending_market,
            remaining_accounts: vec![],
        });

        assert_eq!(ix.accounts.len(), 2);
    }
}
