import { PublicKey, TransactionInstruction } from "@solana/web3.js";
import axios from "axios";

/**
 * Gets a quote from Jupiter for swapping tokens
 */
export async function getQuoteResponseHandler(
    quoteParams: {
        jupiterApiKey: string,
        maxAllowedAccounts: number,
        fromToken: PublicKey,
        toToken: PublicKey,
        amount: number,
        slippageBps: number,
    }
): Promise<any> {
    // Build URL with required parameters
    const baseRequestURL = `${quoteParams.jupiterApiKey}quote?` + 
        `inputMint=${quoteParams.fromToken.toBase58()}&` +
        `outputMint=${quoteParams.toToken.toBase58()}&` +
        `amount=${quoteParams.amount}&` +
        `slippageBps=${quoteParams.slippageBps}`;

    // Add maxAccounts if not using default 64
    const requestURL = quoteParams.maxAllowedAccounts !== 64 
        ? `${baseRequestURL}&maxAccounts=${quoteParams.maxAllowedAccounts}`
        : baseRequestURL;

    try {
        const response = await axios.get(requestURL);
        return response.data;
    } catch (error) {
        //@ts-ignore
        console.log("Jupiter quote error on:", baseRequestURL, error.message);
        return { data: null };
    }
}

/**
 * Converts Jupiter instruction format to Solana TransactionInstruction
 */
export function processInstruction(instruction: {
    programId: string;
    accounts: {
        pubkey: string;
        isSigner: boolean;
        isWritable: boolean;
    }[];
    data: string;
}): TransactionInstruction {
    return {
        programId: new PublicKey(instruction.programId),
        keys: instruction.accounts.map((a: {
            pubkey: string;
            isSigner: boolean;
            isWritable: boolean;
        }) => ({
            ...a,
            pubkey: new PublicKey(a.pubkey)
        })),
        data: Buffer.from(instruction.data, "base64")
    };
}

/**
 * Generates a swap instruction using Jupiter
 */
export async function generateSwapInstruction(
    quoteParams: {
        payer: PublicKey,
        jupiterApiKey: string,
        quoteResponse: any,
    }
): Promise<{
    ix: TransactionInstruction,
    luts: PublicKey[],
}> {
    const {
        payer,
        jupiterApiKey,
        quoteResponse,
    } = quoteParams;

    // Get swap instructions from Jupiter API
    const rawResponse = await axios.post(
        `${jupiterApiKey}swap-instructions`,
        {
            quoteResponse: quoteResponse,
            userPublicKey: payer.toBase58(),
            wrapAndUnwrapSol: false,
        },
        { headers: { 'Content-Type': 'application/json' } }
    ).catch((e: any) => {
        // console.log(e.message, quoteResponse);
        throw new Error("Jupiter swap instructions error");
    });

    const instructions = rawResponse.data;

    return {
        ix: processInstruction(instructions.swapInstruction),
        luts: instructions.addressLookupTableAddresses.map((a: string) => new PublicKey(a)),
    };
}
