use solana_sdk::{
    instruction::{AccountMeta, Instruction},
    pubkey::Pubkey,
};

use crate::constants::Discriminator;
use crate::helpers::derive::get_spread_config_address;
use crate::types::*;

/// Build a SetPoolState instruction (discriminator 21).
///
/// Accounts (2): authority(signer), config(w).
pub fn build_set_pool_state(
    authority: &Pubkey,
    config_pda: &Pubkey,
    params: &SetPoolStateParams,
    program_id: &Pubkey,
) -> Instruction {
    let data = vec![
        Discriminator::SetPoolState as u8,
        params.new_state as u8,
    ];

    let keys = vec![
        AccountMeta::new_readonly(*authority, true),
        AccountMeta::new(*config_pda, false),
    ];

    Instruction {
        program_id: *program_id,
        accounts: keys,
        data,
    }
}

/// Build an UpdateDeltaStaleness instruction (discriminator 15).
///
/// Accounts (2): authority(signer), config(w).
pub fn build_update_delta_staleness(
    authority: &Pubkey,
    config_pda: &Pubkey,
    params: &UpdateDeltaStalenessParams,
    program_id: &Pubkey,
) -> Instruction {
    let data = vec![
        Discriminator::UpdateDeltaStaleness as u8,
        params.delta_staleness,
    ];

    let keys = vec![
        AccountMeta::new_readonly(*authority, true),
        AccountMeta::new(*config_pda, false),
    ];

    Instruction {
        program_id: *program_id,
        accounts: keys,
        data,
    }
}

/// Build a ClosePool instruction (discriminator 20).
///
/// Accounts (10+): authority(signer,w), config(w), midpriceOracle(w), curveMeta(w),
/// curvePrefabs(w), curveUpdates(w), vaultX(w), vaultY(w), tokenProgramX, tokenProgramY,
/// [spreadConfig(w) optional], [authorityAtaX(w) optional], [authorityAtaY(w) optional].
pub fn build_close_pool(
    authority: &Pubkey,
    config_pda: &Pubkey,
    midprice_oracle_pda: &Pubkey,
    curve_meta_pda: &Pubkey,
    curve_prefabs_pda: &Pubkey,
    curve_updates_pda: &Pubkey,
    vault_x: &Pubkey,
    vault_y: &Pubkey,
    token_program_x: &Pubkey,
    token_program_y: &Pubkey,
    spread_config_initialized: bool,
    authority_ata_x: Option<&Pubkey>,
    authority_ata_y: Option<&Pubkey>,
    program_id: &Pubkey,
) -> Instruction {
    let data = vec![Discriminator::ClosePool as u8];

    let mut keys = vec![
        AccountMeta::new(*authority, true),
        AccountMeta::new(*config_pda, false),
        AccountMeta::new(*midprice_oracle_pda, false),
        AccountMeta::new(*curve_meta_pda, false),
        AccountMeta::new(*curve_prefabs_pda, false),
        AccountMeta::new(*curve_updates_pda, false),
        AccountMeta::new(*vault_x, false),
        AccountMeta::new(*vault_y, false),
        AccountMeta::new_readonly(*token_program_x, false),
        AccountMeta::new_readonly(*token_program_y, false),
    ];

    if spread_config_initialized {
        let (spread_config_pda, _) = get_spread_config_address(config_pda, program_id);
        keys.push(AccountMeta::new(spread_config_pda, false));
    }

    if let Some(ata_x) = authority_ata_x {
        keys.push(AccountMeta::new(*ata_x, false));
    }
    if let Some(ata_y) = authority_ata_y {
        keys.push(AccountMeta::new(*ata_y, false));
    }

    Instruction {
        program_id: *program_id,
        accounts: keys,
        data,
    }
}
