use {
    super::executor_simulator::ExecutorBench,
    crate::{
        error::ExecutorError,
        tests::executor_simulator::{
            ExecutorAttack,
            VaaAttack,
        },
    },
    anchor_lang::{
        prelude::{
            Pubkey,
            Rent,
        },
        solana_program::{
            system_instruction::create_account,
            system_program,
        },
    },
    solana_sdk::{
        native_token::LAMPORTS_PER_SOL,
        signature::Keypair,
        signer::Signer,
        system_instruction::transfer,
    },
};

#[tokio::test]
/// This test file tests that the executor can :
/// - Execute instructions with multiple signers
/// - Execute multiple instructions in one call
/// - Protect against replay attacks by updating the claim_record
async fn test_basic_instructions() {
    let mut bench = ExecutorBench::new();

    let emitter = Pubkey::new_unique();

    let executor_key = bench.get_executor_key(&emitter);

    let receiver = Keypair::new();
    let receiver2 = Keypair::new();
    let receiver3 = Keypair::new();
    let receiver4 = Keypair::new();

    let instruction1 = create_account(
        &executor_key,
        &receiver.pubkey(),
        Rent::default().minimum_balance(10),
        10,
        &Pubkey::new_unique(),
    );
    let instruction2 = create_account(
        &executor_key,
        &receiver2.pubkey(),
        Rent::default().minimum_balance(5),
        5,
        &Pubkey::new_unique(),
    );
    let instruction3 = transfer(
        &executor_key,
        &receiver3.pubkey(),
        Rent::default().minimum_balance(0),
    );
    let instruction4 = transfer(
        &executor_key,
        &receiver4.pubkey(),
        Rent::default().minimum_balance(0),
    );

    let vaa_account_create =
        bench.add_vaa_account(&emitter, &vec![instruction1, instruction2], VaaAttack::None);
    let vaa_account_transfer1 =
        bench.add_vaa_account(&emitter, &vec![instruction3], VaaAttack::None);
    let vaa_account_transfer2 =
        bench.add_vaa_account(&emitter, &vec![instruction4], VaaAttack::None);

    let mut sim = bench.start().await;

    // All these accounts are unitialized before we execute the governance messages
    let pre_account = sim.get_account(receiver.pubkey()).await;
    let pre_account2 = sim.get_account(receiver2.pubkey()).await;
    let pre_account3 = sim.get_account(receiver3.pubkey()).await;
    let pre_account4 = sim.get_account(receiver4.pubkey()).await;
    assert_eq!(pre_account, None);
    assert_eq!(pre_account2, None);
    assert_eq!(pre_account3, None);
    assert_eq!(pre_account4, None);

    sim.airdrop(&executor_key, LAMPORTS_PER_SOL).await.unwrap();

    // Execute two account creations in 1 call to the remote executor
    sim.execute_posted_vaa(
        &vaa_account_create,
        &vec![&receiver, &receiver2],
        ExecutorAttack::None,
    )
    .await
    .unwrap();

    // Check state post call
    let post_account = sim.get_account(receiver.pubkey()).await.unwrap();
    assert_eq!(post_account.lamports, Rent::default().minimum_balance(10));
    assert_eq!(post_account.data.len(), 10);

    let post_account2 = sim.get_account(receiver2.pubkey()).await.unwrap();
    assert_eq!(post_account2.lamports, Rent::default().minimum_balance(5));
    assert_eq!(post_account2.data.len(), 5);

    let claim_record_data = sim.get_claim_record(emitter).await;
    assert_eq!(claim_record_data.sequence, 1);

    // Execute one transfer
    sim.execute_posted_vaa(&vaa_account_transfer2, &vec![], ExecutorAttack::None)
        .await
        .unwrap();

    // Check state post call
    let post_account4 = sim.get_account(receiver4.pubkey()).await.unwrap();
    assert_eq!(post_account4.lamports, Rent::default().minimum_balance(0));
    assert_eq!(post_account4.data.len(), 0);
    assert_eq!(post_account4.owner, system_program::id());

    let claim_record_data = sim.get_claim_record(emitter).await;
    assert_eq!(claim_record_data.sequence, 3);

    // Replay attack
    assert_eq!(
        sim.execute_posted_vaa(&vaa_account_transfer2, &vec![], ExecutorAttack::None)
            .await
            .unwrap_err()
            .unwrap(),
        ExecutorError::NonIncreasingSequence.into()
    );

    let claim_record_data = sim.get_claim_record(emitter).await;
    assert_eq!(claim_record_data.sequence, 3);

    // Using a governance message with a lower sequence number
    assert_eq!(
        sim.execute_posted_vaa(&vaa_account_transfer1, &vec![], ExecutorAttack::None)
            .await
            .unwrap_err()
            .unwrap(),
        ExecutorError::NonIncreasingSequence.into()
    );
    let claim_record_data = sim.get_claim_record(emitter).await;
    assert_eq!(claim_record_data.sequence, 3);

    let post_account3 = sim.get_account(receiver3.pubkey()).await;
    assert_eq!(post_account3, None);
}
