use solana_program::instruction::Instruction;
use solana_pubkey::Pubkey;

use crate::{
    operations::{refresh_kamino, KaminoReserveRefresh, RefreshKaminoParams},
    spend_credit_kamino, spend_debit_kamino, SpendCreditKaminoParams, SpendDebitKaminoParams,
};

#[derive(Clone)]
pub struct SpendKaminoAmountsParams {
    pub debit_spend_amount_base_units: u64,
    pub debit_fee_amount_base_units: u64,
    pub credit_spend_amount_base_units: u64,
    pub credit_fee_amount_base_units: u64,
}

#[derive(Clone)]
pub struct SpendKaminoAccountsParams {
    pub owner: Pubkey,
    pub admin: Pubkey,
    pub spend_fee_destination: Pubkey,
    pub liquidity_mint: Pubkey,
    pub liquidity_token_program: Pubkey,
    pub kamino_reserve_liquidity_supply: Pubkey,
    pub kamino_market: Pubkey,
    pub kamino_reserve: Pubkey,
    pub kamino_reserve_collateral_mint: Pubkey,
    pub kamino_reserve_collateral_supply: Pubkey,
    pub collateral_token_program: Pubkey,
    pub borrow_reserve_liquidity_fee_receiver: Pubkey,
    /// Farm state pair for the reserve's collateral farm (`KaminoReserve.farm_collateral`).
    /// Used by `spend_debit_kamino`, which moves collateral. `None` if the reserve has
    /// no collateral farm configured.
    pub kamino_user_farm_state_collateral: Option<Pubkey>,
    pub kamino_reserve_farm_state_collateral: Option<Pubkey>,
    /// Farm state pair for the reserve's debt farm (`KaminoReserve.farm_debt`).
    /// Used by `spend_credit_kamino`, which incurs debt. `None` if the reserve has
    /// no debt farm configured.
    pub kamino_user_farm_state_debt: Option<Pubkey>,
    pub kamino_reserve_farm_state_debt: Option<Pubkey>,
}

pub struct SpendKaminoParams {
    pub amounts: SpendKaminoAmountsParams,
    pub accounts: SpendKaminoAccountsParams,
    pub deposit_reserves: Vec<KaminoReserveRefresh>,
    pub borrow_reserves: Vec<KaminoReserveRefresh>,
}

pub fn spend_kamino(params: &SpendKaminoParams) -> Vec<Instruction> {
    let mut instructions = Vec::new();

    if params.amounts.debit_spend_amount_base_units > 0 {
        let debit_params = SpendDebitKaminoParams {
            spend_amount_base_units: params.amounts.debit_spend_amount_base_units,
            fee_amount_base_units: params.amounts.debit_fee_amount_base_units,
            owner: params.accounts.owner,
            admin: params.accounts.admin,
            spend_fee_destination: params.accounts.spend_fee_destination,
            liquidity_mint: params.accounts.liquidity_mint,
            liquidity_token_program: params.accounts.liquidity_token_program,
            kamino_market: params.accounts.kamino_market,
            kamino_reserve: params.accounts.kamino_reserve,
            kamino_reserve_collateral_mint: params.accounts.kamino_reserve_collateral_mint,
            kamino_reserve_liquidity_supply: params.accounts.kamino_reserve_liquidity_supply,
            kamino_reserve_collateral_supply: params.accounts.kamino_reserve_collateral_supply,
            collateral_token_program: params.accounts.collateral_token_program,
            kamino_user_farm_state: params.accounts.kamino_user_farm_state_collateral,
            kamino_reserve_farm_state: params.accounts.kamino_reserve_farm_state_collateral,
        };
        let debit_ix = spend_debit_kamino(&debit_params);
        instructions.push(debit_ix);
    }

    if params.amounts.credit_spend_amount_base_units > 0 {
        if !instructions.is_empty() {
            // Only need to refresh if spend_debit_kamino was just called
            let obligation = pyra_accounts::get_kamino_obligation(
                &params.accounts.owner,
                &params.accounts.kamino_market,
            );
            let refresh_params = RefreshKaminoParams {
                obligation,
                lending_market: params.accounts.kamino_market,
                deposit_reserves: params.deposit_reserves.clone(),
                borrow_reserves: params.borrow_reserves.clone(),
            };
            let refresh_ixs = refresh_kamino(&refresh_params);
            instructions.extend(refresh_ixs);
        }

        let credit_params = SpendCreditKaminoParams {
            spend_amount_base_units: params.amounts.credit_spend_amount_base_units,
            fee_amount_base_units: params.amounts.credit_fee_amount_base_units,
            owner: params.accounts.owner,
            admin: params.accounts.admin,
            spend_fee_destination: params.accounts.spend_fee_destination,
            liquidity_mint: params.accounts.liquidity_mint,
            liquidity_token_program: params.accounts.liquidity_token_program,
            kamino_reserve_liquidity_supply: params.accounts.kamino_reserve_liquidity_supply,
            kamino_market: params.accounts.kamino_market,
            kamino_reserve: params.accounts.kamino_reserve,
            borrow_reserve_liquidity_fee_receiver: params
                .accounts
                .borrow_reserve_liquidity_fee_receiver,
            kamino_user_farm_state: params.accounts.kamino_user_farm_state_debt,
            kamino_reserve_farm_state: params.accounts.kamino_reserve_farm_state_debt,
        };
        let credit_ix = spend_credit_kamino(&credit_params);
        instructions.push(credit_ix);
    }

    instructions
}
