use inquire::Confirm;
use riptide_amm::{
    fetch_market, quote_exact_in, quote_exact_out, SlippageTolerance, SwapExactIn,
    SwapExactInInstructionArgs, SwapExactOut, SwapExactOutInstructionArgs,
};
use solana_pubkey::Pubkey;
use solana_signer::Signer;
use spl_associated_token_account_interface::address::get_associated_token_address_with_program_id;

use crate::{config::Config, log_context, transaction::process_instructions};

use anyhow::Result;

pub fn handle(
    config: &Config,
    market: &Pubkey,
    amount: &u64,
    amount_is_token_a: &bool,
    amount_is_input: &bool,
    yes: &bool,
) -> Result<()> {
    let rpc = config.rpc_client();

    let market_account = fetch_market(&rpc, market)?;
    let mint_a_account = rpc.get_account(&market_account.data.mint_a)?;
    let mint_b_account = rpc.get_account(&market_account.data.mint_b)?;

    let signer = config
        .keypair()
        .ok_or(anyhow::anyhow!("Signer not found"))?;

    let market_a = get_associated_token_address_with_program_id(
        &market_account.address,
        &market_account.data.mint_a,
        &mint_a_account.owner,
    );
    let market_b = get_associated_token_address_with_program_id(
        &market_account.address,
        &market_account.data.mint_b,
        &mint_b_account.owner,
    );

    let treasury_a_amount = rpc
        .get_token_account_balance(&market_a)?
        .amount
        .parse::<u64>()
        .unwrap();
    let treasury_b_amount = rpc
        .get_token_account_balance(&market_b)?
        .amount
        .parse::<u64>()
        .unwrap();

    let authority_a = get_associated_token_address_with_program_id(
        &signer.pubkey(),
        &market_account.data.mint_a,
        &mint_a_account.owner,
    );
    let authority_b = get_associated_token_address_with_program_id(
        &signer.pubkey(),
        &market_account.data.mint_b,
        &mint_b_account.owner,
    );

    let ix = if *amount_is_input {
        SwapExactIn {
            authority: signer.pubkey(),
            market: *market,
            mint_a: market_account.data.mint_a,
            mint_b: market_account.data.mint_b,
            authority_a,
            authority_b,
            market_a,
            market_b,
            token_program_a: mint_a_account.owner,
            token_program_b: mint_b_account.owner,
            memo_program: spl_memo_interface::v3::ID,
            instructions_sysvar: solana_sdk_ids::sysvar::instructions::ID,
        }
        .instruction(SwapExactInInstructionArgs {
            amount_in: *amount,
            amount_is_token_a: *amount_is_token_a,
            slippage_tolerance: SlippageTolerance::None,
            allow_partial_fill: false,
        })
    } else {
        SwapExactOut {
            authority: signer.pubkey(),
            market: *market,
            mint_a: market_account.data.mint_a,
            mint_b: market_account.data.mint_b,
            authority_a,
            authority_b,
            market_a,
            market_b,
            token_program_a: mint_a_account.owner,
            token_program_b: mint_b_account.owner,
            memo_program: spl_memo_interface::v3::ID,
            instructions_sysvar: solana_sdk_ids::sysvar::instructions::ID,
        }
        .instruction(SwapExactOutInstructionArgs {
            amount_out: *amount,
            amount_is_token_a: *amount_is_token_a,
            slippage_tolerance: SlippageTolerance::None,
            allow_partial_fill: false,
        })
    };

    let quote = if *amount_is_input {
        quote_exact_in(
            *amount,
            *amount_is_token_a,
            &market_account.data.oracle,
            treasury_a_amount,
            treasury_b_amount,
            market_account.data.skew_cliff_min_per_m,
            market_account.data.skew_cliff_max_per_m,
        )
        .map_err(|e| anyhow::anyhow!("Failed to quote exact in: {}", e))?
    } else {
        quote_exact_out(
            *amount,
            *amount_is_token_a,
            &market_account.data.oracle,
            treasury_a_amount,
            treasury_b_amount,
            market_account.data.skew_cliff_min_per_m,
            market_account.data.skew_cliff_max_per_m,
        )
        .map_err(|e| anyhow::anyhow!("Failed to quote exact out: {}", e))?
    };

    let (mint_in, mint_out) = if *amount_is_token_a {
        (market_account.data.mint_a, market_account.data.mint_b)
    } else {
        (market_account.data.mint_b, market_account.data.mint_a)
    };

    log_context!(
      market => market,
      mint_in => mint_in,
      mint_out => mint_out,
      amount_in => quote.amount_in,
      amount_out => quote.amount_out,
    );

    let confirmed = if *yes {
        true
    } else {
        Confirm::new("Execute transaction?")
            .with_default(false)
            .prompt()
            .unwrap()
    };

    if confirmed {
        process_instructions(&rpc, &[signer], &[ix])?;
    }

    Ok(())
}
