import { BN, Program } from "@project-serum/anchor";
import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID } from "@solana/spl-token";
import {
	PublicKey,
	SystemProgram,
	SYSVAR_CLOCK_PUBKEY,
	SYSVAR_RENT_PUBKEY,
	TransactionInstruction,
} from "@solana/web3.js";

import { ISolmashWhitelistInstructions } from "./definitions";
import { SolmashWhitelist } from "./idl";

export class SolmashWhitelistInstructions implements ISolmashWhitelistInstructions {
	constructor(readonly program: Program<SolmashWhitelist>) {}

	async getInitAuctionInstruction(
		auction: PublicKey,
		auctionVault: PublicKey,
		owner: PublicKey,
		initParams: {
			name: string;
			enabled: boolean;
			preSaleStartTime: BN;
			preSaleEndTime: BN;
			ticketsInPool: BN;
			tokenQuantityPerTicket: BN;
			ticketPriceInSol: BN;
			ticketPriceInSobb: BN;
			auctionToken: PublicKey;
			bidToken: PublicKey;
		},
	): Promise<TransactionInstruction> {
		const {
			name,
			enabled,
			preSaleStartTime,
			preSaleEndTime,
			ticketsInPool,
			tokenQuantityPerTicket,
			ticketPriceInSol,
			ticketPriceInSobb,
			auctionToken,
			bidToken,
		} = initParams;

		return this.program.methods
			.initAuction({
				auctionToken,
				bidToken,
				enabled,
				name,
				preSaleEndTime,
				preSaleStartTime,
				ticketPriceInSobb,
				ticketPriceInSol,
				ticketsInPool,
				tokenQuantityPerTicket,
			})
			.accounts({
				auction,
				auctionVault,
				owner,
				rent: SYSVAR_RENT_PUBKEY,
				systemProgram: SystemProgram.programId,
			})
			.instruction();
	}

	async getAddTokenInstruction(
		auction: PublicKey,
		auctionToken: PublicKey,
		auctionVault: PublicKey,
		auctionVaultTokenAccount: PublicKey,
		owner: PublicKey,
		ownerAuctionTokenAccount: PublicKey,
	): Promise<TransactionInstruction> {
		return this.program.methods
			.addToken()
			.accounts({
				associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
				auction,
				auctionToken,
				auctionVault,
				auctionVaultTokenAccount,
				clock: SYSVAR_CLOCK_PUBKEY,
				owner,
				ownerAuctionTokenAccount,
				rent: SYSVAR_RENT_PUBKEY,
				systemProgram: SystemProgram.programId,
				tokenProgram: TOKEN_PROGRAM_ID,
			})
			.instruction();
	}

	async getWithdrawFundsInstruction(
		auction: PublicKey,
		auctionBidTokenAccount: PublicKey,
		auctionToken: PublicKey,
		auctionVault: PublicKey,
		auctionVaultTokenAccount: PublicKey,
		bidToken: PublicKey,
		creator: PublicKey,
		creatorAuctionTokenAccount: PublicKey,
		creatorBidTokenAccount: PublicKey,
	): Promise<TransactionInstruction> {
		return this.program.methods
			.withdrawFunds()
			.accounts({
				auction,
				auctionBidTokenAccount,
				auctionToken,
				auctionVault,
				auctionVaultTokenAccount,
				bidToken,
				clock: SYSVAR_CLOCK_PUBKEY,
				creator,
				creatorAuctionTokenAccount,
				creatorBidTokenAccount,
				systemProgram: SystemProgram.programId,
				tokenProgram: TOKEN_PROGRAM_ID,
			})
			.instruction();
	}

	async getWhitelistInstruction(
		auction: PublicKey,
		buyerPda: PublicKey,
		creator: PublicKey,
		whitelistUser: PublicKey,
	): Promise<TransactionInstruction> {
		return this.program.methods
			.whitelist()
			.accounts({
				auction,
				buyerPda,
				creator,
				rent: SYSVAR_RENT_PUBKEY,
				systemProgram: SystemProgram.programId,
				whitelistUser,
			})
			.instruction();
	}

	async getPreSaleBuyUsingSplInstruction(
		auction: PublicKey,
		auctionVault: PublicKey,
		auctionVaultBidTokenAccount: PublicKey,
		bidToken: PublicKey,
		buyer: PublicKey,
		buyerBidTokenAccount: PublicKey,
		buyerPda: PublicKey,
	): Promise<TransactionInstruction> {
		return this.program.methods
			.preSaleBuyUsingSpl()
			.accounts({
				associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
				auction,
				auctionVault,
				auctionVaultBidTokenAccount,
				bidToken,
				buyer,
				buyerBidTokenAccount,
				buyerPda,
				clock: SYSVAR_CLOCK_PUBKEY,
				systemProgram: SystemProgram.programId,
				tokenProgram: TOKEN_PROGRAM_ID,
			})
			.instruction();
	}

	async getPreSaleBuyUsingSolInstruction(
		auction: PublicKey,
		auctionVault: PublicKey,
		buyer: PublicKey,
		buyerPda: PublicKey,
	): Promise<TransactionInstruction> {
		return this.program.methods
			.preSaleBuyUsingSol()
			.accounts({
				auction,
				auctionVault,
				buyer,
				buyerPda,
				clock: SYSVAR_CLOCK_PUBKEY,
				systemProgram: SystemProgram.programId,
			})
			.instruction();
	}

	async getClaimTokensInstruction(
		auction: PublicKey,
		auctionToken: PublicKey,
		auctionVault: PublicKey,
		auctionVaultTokenAccount: PublicKey,
		buyer: PublicKey,
		buyerAuctionTokenAccount: PublicKey,
		buyerPda: PublicKey,
	): Promise<TransactionInstruction> {
		return this.program.methods
			.claimTokens()
			.accounts({
				associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
				auction,
				auctionToken,
				auctionVault,
				auctionVaultTokenAccount,
				buyer,
				buyerAuctionTokenAccount,
				buyerPda,
				clock: SYSVAR_CLOCK_PUBKEY,
				systemProgram: SystemProgram.programId,
				tokenProgram: TOKEN_PROGRAM_ID,
			})
			.instruction();
	}

	async getChangeTimeInstruction(
		auction: PublicKey,
		creator: PublicKey,
		newStartTime: BN,
		newEndTime: BN,
	): Promise<TransactionInstruction> {
		return this.program.methods
			.changeTime(newStartTime, newEndTime)
			.accounts({
				auction,
				creator,
			})
			.instruction();
	}
}
