#[allow(deprecated)]
use solana_sdk::system_program;
use solana_sdk::{
    instruction::{AccountMeta, Instruction},
    pubkey::Pubkey,
};

use crate::constants::Discriminator;
use crate::helpers::derive::{get_fee_config_address, get_pool_fee_config_address};
use crate::types::*;

/// Build an InitializeFeeConfig instruction (discriminator 6).
///
/// Accounts (4): payer(signer,w), authority(signer), feeConfig(w), systemProgram.
pub fn build_initialize_fee_config(
    payer: &Pubkey,
    authority: &Pubkey,
    params: &InitializeFeeConfigParams,
    program_id: &Pubkey,
) -> Instruction {
    let (fee_config_pda, _) = get_fee_config_address(program_id);

    let mut data = Vec::with_capacity(69);
    data.push(Discriminator::InitializeFeeConfig as u8);
    data.extend_from_slice(&params.fee_ppm.to_le_bytes());
    data.extend_from_slice(params.fee_admin.as_ref());
    data.extend_from_slice(params.fee_recipient.as_ref());

    let keys = vec![
        AccountMeta::new(*payer, true),
        AccountMeta::new_readonly(*authority, true),
        AccountMeta::new(fee_config_pda, false),
        AccountMeta::new_readonly(system_program::id(), false),
    ];

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

/// Build an UpdateFeeConfig instruction (discriminator 7).
///
/// Accounts (2): feeAdmin(signer), feeConfig(w).
pub fn build_update_fee_config(
    fee_admin: &Pubkey,
    params: &UpdateFeeConfigParams,
    program_id: &Pubkey,
) -> Instruction {
    let (fee_config_pda, _) = get_fee_config_address(program_id);

    let fee_ppm = params.fee_ppm.unwrap_or(u32::MAX);
    let fee_recipient = params.fee_recipient.unwrap_or(Pubkey::default());

    let mut data = Vec::with_capacity(37);
    data.push(Discriminator::UpdateFeeConfig as u8);
    data.extend_from_slice(&fee_ppm.to_le_bytes());
    data.extend_from_slice(fee_recipient.as_ref());

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

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

/// Build a RotateFeeAdmin instruction (discriminator 24).
///
/// Accounts (2): feeAdmin(signer), feeConfig(w).
pub fn build_rotate_fee_admin(
    fee_admin: &Pubkey,
    params: &RotateFeeAdminParams,
    program_id: &Pubkey,
) -> Instruction {
    let (fee_config_pda, _) = get_fee_config_address(program_id);

    let mut data = Vec::with_capacity(33);
    data.push(Discriminator::RotateFeeAdmin as u8);
    data.extend_from_slice(params.new_fee_admin.as_ref());

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

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

/// Build an InitializePoolFeeConfig instruction (discriminator 25).
///
/// Accounts (6): payer(signer,w), feeAdmin(signer), config(w), poolFeeConfig(w), globalFeeConfig, systemProgram.
pub fn build_initialize_pool_fee_config(
    payer: &Pubkey,
    fee_admin: &Pubkey,
    config_pda: &Pubkey,
    params: &InitializePoolFeeConfigParams,
    program_id: &Pubkey,
) -> Instruction {
    let (pool_fee_config_pda, _) = get_pool_fee_config_address(config_pda, program_id);
    let (global_fee_config_pda, _) = get_fee_config_address(program_id);

    let mut data = Vec::with_capacity(69);
    data.push(Discriminator::InitializePoolFeeConfig as u8);
    data.extend_from_slice(&params.fee_ppm.to_le_bytes());
    data.extend_from_slice(params.fee_admin.as_ref());
    data.extend_from_slice(params.fee_recipient.as_ref());

    let keys = vec![
        AccountMeta::new(*payer, true),
        AccountMeta::new_readonly(*fee_admin, true),
        AccountMeta::new(*config_pda, false),
        AccountMeta::new(pool_fee_config_pda, false),
        AccountMeta::new_readonly(global_fee_config_pda, false),
        AccountMeta::new_readonly(system_program::id(), false),
    ];

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

/// Build an UpdatePoolFeeConfig instruction (discriminator 26).
///
/// Accounts (3): feeAdmin(signer), config, poolFeeConfig(w).
pub fn build_update_pool_fee_config(
    fee_admin: &Pubkey,
    config_pda: &Pubkey,
    params: &UpdatePoolFeeConfigParams,
    program_id: &Pubkey,
) -> Instruction {
    let (pool_fee_config_pda, _) = get_pool_fee_config_address(config_pda, program_id);

    let fee_ppm = params.fee_ppm.unwrap_or(u32::MAX);
    let fee_recipient = params.fee_recipient.unwrap_or(Pubkey::default());

    let mut data = Vec::with_capacity(37);
    data.push(Discriminator::UpdatePoolFeeConfig as u8);
    data.extend_from_slice(&fee_ppm.to_le_bytes());
    data.extend_from_slice(fee_recipient.as_ref());

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

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