// Core dependencies
import { Program } from "@coral-xyz/anchor";
import { Connection, Keypair, PublicKey } from "@solana/web3.js";

// Local imports
import { BasketsProgram } from "./idl/types";
import { createBasketIxs } from "./instructions/creator/createBasket";
import { getAccountInfos } from "./utils/programAccounts";
import { prepareV0Transactions, VersionedTxs } from "./utils/txUtils";
import { editBasketIx } from "./instructions/creator/editBasket";
import { validateOracle } from "./utils/oracle";
import { addNewTokenIx } from "./instructions/manager/addNewToken";
import { removeTokenIx } from "./instructions/manager/removeToken";
import { updateTokenWeightsIx } from "./instructions/manager/updateTokenWeights";
import { updatePythSponsoredFeedsIx } from "./instructions/oracleUpdate/updatePythSponsoredFeeds";
import { createPythSponsoredFeedsIxs } from "./instructions/oracleUpdate/createPythSponsoredFeeds";
import { addLamportsForAutomationIx } from "./instructions/manager/addLamportsForAutomation";

export interface CreateBasketParams {
    basketType: number,
    creatorDepositFeeBps: number,
    creatorManagementFeeBps: number,
    creatorPerformanceFeeBps: number,
    host: PublicKey,
    hostDepositFeeBps: number,
    hostManagementFeeBps: number,
    hostPerformanceFeeBps: number,
    managers: PublicKey[],
    managersWeightBps: number[],
    managersAuthority: number[],
    managersDepositFeeBps: number,
    managersManagementFeeBps: number,
    managersPerformanceFeeBps: number,
    basketDepositFeeBps: number,
    basketWithdrawFeeBps: number,
    rebalanceIntervalSeconds: number,
    rebalanceThresholdBps: number,
    rebalanceSlippageBps: number,
    lpThresholdBps: number,
    allowAutomation: boolean,
    allowLp: boolean,
    name: string,
    symbol: string,
    uri: string,
}

export interface EditBasketParams extends CreateBasketParams {
    basket: PublicKey,
}

export async function createBasketHandler(
    sdkParams: {
        payer: PublicKey,
        connection: Connection,
        program: Program<BasketsProgram>,
        priorityFee: number,
    },
    params: CreateBasketParams,
    basketKeypair: Keypair,
): Promise<VersionedTxs>{
    const ixs = await createBasketIxs({
        ...params,
        basketKeypair: basketKeypair,
        creator: sdkParams.payer,
        program: sdkParams.program,
    });
    return await prepareV0Transactions({
        connection: sdkParams.connection,
        payer: sdkParams.payer,
        priorityFee: sdkParams.priorityFee,
        multipleIxs: [ixs],
        multipleLookupTableAddresses: [[]],
        signers: [[basketKeypair]],
        batches: [1],
    });
}

export async function editBasketSettingsHandler(
    sdkParams: {
        payer: PublicKey,
        connection: Connection,
        program: Program<BasketsProgram>,
        priorityFee: number,
    },
    params: EditBasketParams,
): Promise<VersionedTxs> {
    const ix = await editBasketIx({
        ...params,
        creator: sdkParams.payer,
        program: sdkParams.program,
    });
    return await prepareV0Transactions({
        connection: sdkParams.connection,
        payer: sdkParams.payer,
        priorityFee: sdkParams.priorityFee,
        multipleIxs: [[ix]],
        multipleLookupTableAddresses: [[]],
        signers: [[]],
        batches: [1],
    });
}

export async function addLamportsForAutomationHandler(
    sdkParams: {
        payer: PublicKey,
        connection: Connection,
        program: Program<BasketsProgram>,
        priorityFee: number,
    },
    params: {
        basket: PublicKey;
        amount: number;
    },
): Promise<VersionedTxs> {
    const ix = await addLamportsForAutomationIx({
        program: sdkParams.program,
        basket: params.basket,
        manager: sdkParams.payer,
        amount: params.amount,
    });
    return await prepareV0Transactions({
        connection: sdkParams.connection,
        payer: sdkParams.payer,
        priorityFee: sdkParams.priorityFee,
        multipleIxs: [[ix]],
        multipleLookupTableAddresses: [[]],
        signers: [[]],
        batches: [1],
    });
}

export async function addNewTokenHandler(
    sdkParams: {
        payer: PublicKey,
        connection: Connection,
        program: Program<BasketsProgram>,
        priorityFee: number,
    },
    params: {
        basket: PublicKey;
        token: PublicKey;
        tokenWeight: number;
        oracleType: number;
        oraclePool: PublicKey;
        oracle1: PublicKey;
        oracle2: PublicKey;
    },
): Promise<VersionedTxs> {
    const accountInfos = await getAccountInfos(
        sdkParams.connection,
        [ params.token, params.oraclePool ],
    );
    const validation = validateOracle(
        sdkParams.program,
        params.token, //@ts-ignore
        accountInfos[0],
        accountInfos[1],
        params.oracleType,
        params.oracle1,
        params.oracle2,
    );
    if (validation != "OK")
        throw new Error("Error: " + validation);
    const ix = await addNewTokenIx({
        program: sdkParams.program,
        manager: sdkParams.payer,
        ...params,
    });
    return await prepareV0Transactions({
        connection: sdkParams.connection,
        payer: sdkParams.payer,
        priorityFee: sdkParams.priorityFee,
        multipleIxs: [[ix]],
        multipleLookupTableAddresses: [[]],
        signers: [[]],
        batches: [1],
    });
}

export async function removeTokenHandler(
    sdkParams: {
        payer: PublicKey,
        connection: Connection,
        program: Program<BasketsProgram>,
        priorityFee: number,
    },
    params: {
        basket: PublicKey;
        token: PublicKey;
    },
): Promise<VersionedTxs> {
    const ix = await removeTokenIx({
        program: sdkParams.program,
        manager: sdkParams.payer,
        ...params,
    });
    return await prepareV0Transactions({
        connection: sdkParams.connection,
        payer: sdkParams.payer,
        priorityFee: sdkParams.priorityFee,
        multipleIxs: [[ix]],
        multipleLookupTableAddresses: [[]],
        signers: [[]],
        batches: [1],
    });
}

export async function updateTokenWeightsHandler(
    sdkParams: {
        payer: PublicKey,
        connection: Connection,
        program: Program<BasketsProgram>,
        priorityFee: number,
    },
    params: {
        basket: PublicKey;
        tokenWeights: number[];
        writeVersion: number;
    },
): Promise<VersionedTxs> {
    const ix = await updateTokenWeightsIx({
        program: sdkParams.program,
        manager: sdkParams.payer,
        ...params,
    });
    return await prepareV0Transactions({
        connection: sdkParams.connection,
        payer: sdkParams.payer,
        priorityFee: sdkParams.priorityFee,
        multipleIxs: [[ix]],
        multipleLookupTableAddresses: [[]],
        signers: [[]],
        batches: [1],
    });
}

export async function createPythSponsoredFeedsHandler(sdkParams: {
    payer: PublicKey,
    connection: Connection,
    program: Program<BasketsProgram>,
    priorityFee: number,
}): Promise<VersionedTxs> {
    const pythSponsoredFeedsKeypair = Keypair.generate();
    const ixs = await createPythSponsoredFeedsIxs({
        program: sdkParams.program,
        payer: sdkParams.payer,
        pythSponsoredFeeds: pythSponsoredFeedsKeypair.publicKey,
    });
    return await prepareV0Transactions({
        connection: sdkParams.connection,
        payer: sdkParams.payer,
        priorityFee: sdkParams.priorityFee,
        multipleIxs: [ixs],
        multipleLookupTableAddresses: [[]],
        signers: [[pythSponsoredFeedsKeypair]],
        batches: [1],
    });
}

export async function updatePythSponsoredFeedsHandler(
    sdkParams: {
        payer: PublicKey,
        connection: Connection,
        program: Program<BasketsProgram>,
        priorityFee: number,
    },
    params: {
        tokenMint: PublicKey;
        feedAccount: PublicKey;
        isActive: boolean;
    },
): Promise<VersionedTxs> {
    const ix = await updatePythSponsoredFeedsIx({
        program: sdkParams.program,
        payer: sdkParams.payer,
        ...params,
    });
    return await prepareV0Transactions({
        connection: sdkParams.connection,
        payer: sdkParams.payer,
        priorityFee: sdkParams.priorityFee,
        multipleIxs: [[ix]],
        multipleLookupTableAddresses: [[]],
        signers: [[]],
        batches: [1],
    });
}
