import { PublicKey } from '@solana/web3.js';
import { getAccount, getAssociatedTokenAddressSync, TokenAccountNotFoundError, Account } from '@solana/spl-token';
import { ETFInvalidParamsError, ETFInsufficientBalanceError, ETFNotExistsError } from './error';
import { DexClient } from '../core/client';
// Start of Selection
import { ETFInfo } from './queries';
import { ETFCreateParams, MintETFTokenParams } from '../types/params';

export async function checkATAExists(client: DexClient, tokens: PublicKey[], owner: PublicKey, isContract: boolean = false): Promise<Account | undefined> {
    for (const token of tokens) {
        const tokenAccountAddress = getAssociatedTokenAddressSync(token, owner, isContract);
        try {
            return await getAccount(client.connection, tokenAccountAddress);
        } catch (e) {
            if (e instanceof TokenAccountNotFoundError) {
                throw new TokenAccountNotFoundError(`账户 ${tokenAccountAddress.toBase58()} 不存在`);
            }
            throw e;
        }
    }
    return undefined;
}

export async function checkBalance(client: DexClient, etfAddress: PublicKey, owner: PublicKey, requiredAmount: number): Promise<void> {
    const balance = await client.queries.getETFBalance(etfAddress, owner);
    if (balance < requiredAmount) {
        throw new ETFInsufficientBalanceError(`用户代币账户余额不足，所需: ${requiredAmount}, 当前: ${balance}`);
    }
}

export async function checkETFExists(client: DexClient, etfAddress: PublicKey): Promise<ETFInfo> {
    const etfInfo = await client.queries.getETFInfo(etfAddress);
    if (typeof etfInfo === 'string') {
        throw new ETFNotExistsError(etfInfo);
    }
    return etfInfo;
}

export function validateETFCreateParams(params: ETFCreateParams): void {
    const { assets } = params;

    // check if the constituent is not empty
    if (!assets?.length) {
        throw new ETFInvalidParamsError('ETF组成部分不能为空');
    }

    const totalWeight = assets.reduce((sum, item) => sum + item.weight, 0);
    if (totalWeight !== 100) {
        throw new ETFInvalidParamsError(`ETF组成部分权重之和必须为100，当前为: ${totalWeight}`);
    }

    // check if the token address is valid
    const tokenSet = new Set<string>();
    for (const item of assets) {
        let tokenKey: PublicKey;
        try {
            tokenKey = new PublicKey(item.token);
        } catch (e) {
            throw new ETFInvalidParamsError(`无效的代币地址: ${item.token}`);
        }

        // check if there are duplicate tokens
        if (tokenSet.has(tokenKey.toBase58())) {
            throw new ETFInvalidParamsError(`存在重复的代币地址: ${item.token}`);
        }
        tokenSet.add(tokenKey.toBase58());
        if (item.weight <= 0) {
            throw new ETFInvalidParamsError(`代币权重必须大于0: ${item.token}`);
        }
    }
}

export function validateMintETFParams(params: MintETFTokenParams): void {
    if (params.lamports <= 0) {
        throw new ETFInvalidParamsError('购买份额必须大于0');
    }
    // maximum limit 1000000 SOL
    if (params.lamports > 1e15) { // 1000000 * 1e9
        throw new ETFInvalidParamsError('购买份额超过最大限制（1000000 SOL）');
    }
} 