import assert from "assert";
import dotenv from "dotenv";

import * as anchor from "@project-serum/anchor";
import { AnchorProvider, Wallet } from "@project-serum/anchor";
import {
	clusterApiUrl,
	Connection,
	Keypair,
	Transaction,
	VersionedTransaction,
} from "@solana/web3.js";

dotenv.config();

export function getConnection(cluster?: anchor.web3.Cluster) {
	let endpoint: string;

	if (cluster && cluster === "devnet") {
		endpoint = clusterApiUrl("devnet");
	} else {
		const RPC_URL = process.env.RPC_URL;

		if (RPC_URL) {
			endpoint = RPC_URL;
		} else {
			endpoint = clusterApiUrl(cluster);
		}
	}

	return new Connection(endpoint);
}

export function getProviders(conn?: anchor.web3.Connection) {
	const SECRET_KEYS = process.env.SECRET_KEYS;

	assert(SECRET_KEYS && SECRET_KEYS != "", "missing env var: SECRET_KEYS");
	const keypairs: Keypair[] = [];
	try {
		const secretKeys = JSON.parse(SECRET_KEYS);

		assert(Array.isArray(secretKeys), "Invalid format for SECRET_KEYS");

		for (const keys of secretKeys) {
			// console.log("secret key", keys);
			assert(keys && typeof keys === "string" && keys != "", "Invalid secret key");

			const keypair = Keypair.fromSecretKey(anchor.utils.bytes.bs58.decode(keys));

			keypairs.push(keypair);
		}
	} catch (err: any) {
		throw new Error("Some error occured parsing secret key: " + err.message);
	}

	const providers: AnchorProvider[] = [];

	for (const keypair of keypairs) {
		const connection = conn ? conn : getConnection();

		const provider = new AnchorProvider(
			connection,
			new Wallet(keypair),
			AnchorProvider.defaultOptions(),
		);
		providers.push(provider);
	}

	return providers;
}

export function nowInSec() {
	return Math.floor(Date.now() / 1000);
}

export function getSignTransaction(provider: AnchorProvider) {
	const signTransaction = async <T extends Transaction | VersionedTransaction>(
		transaction: T,
	): Promise<T> => {
		assert(transaction instanceof Transaction, "Transaction is not legacy.");
		await provider.wallet.signTransaction(transaction);
		return transaction;
	};

	return signTransaction;
}
