// Core dependencies
import { BN, Program } from "@coral-xyz/anchor";
import { AddressLookupTableProgram, Keypair, PublicKey, SystemProgram, SYSVAR_RENT_PUBKEY, TransactionInstruction } from "@solana/web3.js";
import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID } from "@solana/spl-token";

// Local imports
import { BasketsProgram } from "../../idl/types";
import {
    getAta,
    getBasketPda,
    getBasketTokenMintAccount,
    getDeactivatedLookupTableAccount,
    getLookupTableAccount,
    getMetadataAccount,
} from "../../utils/programAccounts";
import { METADATA_PROGRAM_ID } from "../../utils/metadataUtils";
import { BASKETS_STATE_SIZE } from "../../state/basket";
import { WSOL_MINT } from "../../utils/constants";


export async function createBasketIxs(params: {
    program: Program<BasketsProgram>,
    creator: PublicKey,
    basketKeypair: Keypair,
    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,
}): Promise<TransactionInstruction[]> {
    // Destructure program from params
    const { program } = params;

    while (params.managers.length < 3) {
        params.managers.push(PublicKey.default);
    }
    while (params.managersWeightBps.length < 3) {
        params.managersWeightBps.push(0);
    }
    while (params.managersAuthority.length < 3) {
        params.managersAuthority.push(0);
    }

    // Generate basket seed and derive basket-related accounts
    const basket = params.basketKeypair.publicKey;
    const basketPda = getBasketPda(basket);
    const tokenMint = getBasketTokenMintAccount(basket);
    const metadataAccount = getMetadataAccount(tokenMint);
    
    // Get current slot and derive lookup table accounts
    const slot = await program.provider.connection.getSlot();
    const creator = params.creator;
    const lookupTable1 = getLookupTableAccount(basketPda, slot);
    const lookupTable2 = getLookupTableAccount(basketPda, slot - 1);
    const deactivatedLookupTable1 = getDeactivatedLookupTableAccount(lookupTable1);
    const deactivatedLookupTable2 = getDeactivatedLookupTableAccount(lookupTable2);

    const initIx = SystemProgram.createAccount({
        fromPubkey: creator,
        newAccountPubkey: basket,
        space: 8 + BASKETS_STATE_SIZE,
        lamports: await program.provider.connection.getMinimumBalanceForRentExemption(8 + BASKETS_STATE_SIZE),
        programId: program.programId,
    });

    const createBasketIx = await program.methods
        .createBasket(
            params.basketType,
            params.creatorDepositFeeBps,
            params.creatorManagementFeeBps,
            params.creatorPerformanceFeeBps,
            params.host,
            params.hostDepositFeeBps,
            params.hostManagementFeeBps,
            params.hostPerformanceFeeBps,
            params.managers[0],
            params.managersWeightBps[0],
            params.managersAuthority[0],
            params.managersDepositFeeBps,
            params.managersManagementFeeBps,
            params.managersPerformanceFeeBps,
            params.basketDepositFeeBps,
            params.basketWithdrawFeeBps,
            new BN(params.rebalanceIntervalSeconds),
            params.rebalanceThresholdBps,
            params.rebalanceSlippageBps,
            params.lpThresholdBps,
            params.allowAutomation ? 1 : 0,
            params.allowLp ? 1 : 0,
            {
                name: params.name,
                symbol: params.symbol,
                uri: params.uri,
            }
        )
        .accountsStrict({
            creator,
            basket,
            basketPda,
            tokenMint,
            metadataAccount,
            metadataProgram: METADATA_PROGRAM_ID,
            tokenProgram: TOKEN_PROGRAM_ID,
            associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
            systemProgram: SystemProgram.programId,
            rent: SYSVAR_RENT_PUBKEY,
        })
        .instruction();

    const createLutsIx = await program.methods
        .createLutsForBasket(
            new BN(slot),
        )
        .accountsStrict({
            creator,
            basket,
            basketPda,
            lookupTable1,
            lookupTable2,
            deactivatedLookupTable1,
            deactivatedLookupTable2,
            newTokenBasketTokenAccount: getAta(basketPda, WSOL_MINT),
            wsolTokenMint: WSOL_MINT,
            addressLookupTableProgram: AddressLookupTableProgram.programId,
            systemProgram: SystemProgram.programId,
            associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
            tokenProgram: TOKEN_PROGRAM_ID,
            rent: SYSVAR_RENT_PUBKEY,
        })
        .instruction();

    return [initIx, createBasketIx, createLutsIx];
}
