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

pub struct FulfillUpdateSpendLimitsParams {
    pub spend_limits_order: Pubkey,
    pub owner: Pubkey,
    pub order_payer: Pubkey,
    pub admin: Pubkey,
    /// Whether `admin` is signing this transaction. The on-chain handler reads
    /// `admin.is_signer` at runtime via `is_admin_signer` to decide whether to
    /// skip the time-lock delay. Anchor's auto-derived metas mark `admin` as a
    /// non-signer (it's an `UncheckedAccount`), so callers acting as admin
    /// must set this to `true` to include `admin` in the message's required
    /// signer set.
    pub admin_is_signer: bool,
}

pub fn fulfill_update_spend_limits(params: &FulfillUpdateSpendLimitsParams) -> Instruction {
    let vault = pyra_accounts::get_vault(&params.owner);

    let mut accounts = crate::pyra_program::client::accounts::FulfillUpdateSpendLimits {
        spend_limits_order: params.spend_limits_order,
        vault,
        order_payer: params.order_payer,
        admin: params.admin,
    }
    .to_account_metas(None);

    if params.admin_is_signer {
        // `FulfillUpdateSpendLimits` always emits an `admin` meta. Crash loudly
        // if that invariant ever breaks so callers don't silently ship a
        // non-signing transaction.
        #[allow(clippy::expect_used)]
        let meta = accounts
            .iter_mut()
            .find(|m| m.pubkey == params.admin)
            .expect("FulfillUpdateSpendLimits metas must include admin");
        meta.is_signer = true;
    }

    let data = crate::pyra_program::client::args::FulfillUpdateSpendLimits {}.data();

    Instruction {
        program_id: pyra_accounts::PYRA_PROGRAM_ID,
        accounts,
        data,
    }
}

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

    fn base_params(admin_is_signer: bool) -> FulfillUpdateSpendLimitsParams {
        FulfillUpdateSpendLimitsParams {
            spend_limits_order: pubkey!("BAtFj4kQttZRVep3UZS2aZRDixkGYgWsbqTBVDbnSsPF"),
            owner: pubkey!("d4A2prbA2whesmvHaL88BH6Ewn5N4bTSjm4GiKy2eSi"),
            order_payer: pubkey!("7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF"),
            admin: pubkey!("Gnt27xtC473ZT2Mw5u8wZ68Z3gULkSTb5DuxJy7eJotD"),
            admin_is_signer,
        }
    }

    #[test]
    fn admin_not_signer_keeps_admin_meta_unsigned() {
        let params = base_params(false);
        let ix = fulfill_update_spend_limits(&params);

        let admin_meta = ix
            .accounts
            .iter()
            .find(|m| m.pubkey == params.admin)
            .unwrap();
        assert!(!admin_meta.is_signer);
        assert!(!ix.data.is_empty());
    }

    #[test]
    fn admin_is_signer_flips_admin_meta() {
        let params = base_params(true);
        let ix = fulfill_update_spend_limits(&params);

        let admin_meta = ix
            .accounts
            .iter()
            .find(|m| m.pubkey == params.admin)
            .unwrap();
        assert!(admin_meta.is_signer);
    }
}
