import { BN, Program } from "@coral-xyz/anchor";
import { AccountInfo, GetProgramAccountsResponse, PublicKey } from "@solana/web3.js";
import { FundsIDL } from "./fundsIDL";
import { CurveChainData, FundStateChainData, OraclePrice, TokenSettings } from "./types";
import { parsePriceData } from "@pythnetwork/client";

export function asciiToString(
    coingeckoIdAscii: number[],
): string {
    let coingeckoId: string = "";
    for(let i=0; i<coingeckoIdAscii.length; i++)
        if(coingeckoIdAscii[i] != 0)
            coingeckoId += String.fromCharCode(coingeckoIdAscii[i]).toString();
    return coingeckoId;
}

export function decodeTokenList(
    program: Program<FundsIDL>,
    tokenListAccountInfo: AccountInfo<Buffer>,
): TokenSettings[] {
    let state = program.coder.accounts.decode("tokenList", tokenListAccountInfo.data);
    let numTokens = state.numTokens.toNumber();
    let tokens = [];
    for (let i = 0; i < numTokens; i++) {
        let tokenSettings = state.list[i];
        tokens.push({
            id: i,
            symbol: "",
            name: "",
            tokenMint: tokenSettings.tokenMint.toBase58(),
            decimals: tokenSettings.decimals,
            coingeckoId: asciiToString(tokenSettings.coingeckoId),
            pdaTokenAccount: tokenSettings.pdaTokenAccount.toBase58(),
            oracleType: tokenSettings.oracleType == 0 ? "Pyth" : "Switchboard",
            oracleAccount: tokenSettings.oracleAccount.toBase58(),
            oracleIndex: tokenSettings.oracleIndex,
            oracleConfidencePct: tokenSettings.oracleConfidencePct,
            fixedConfidenceBps: tokenSettings.fixedConfidenceBps,
            tokenSwapFeeBeforeTwBps: tokenSettings.tokenSwapFeeBeforeTwBps,
            tokenSwapFeeAfterTwBps: tokenSettings.tokenSwapFeeAfterTwBps,
            isLive: tokenSettings.isLive == 0 ? false : true,
            lpOn: tokenSettings.lpOn == 0 ? false : true,
            useCurveData: tokenSettings.useCurveData == 0 ? false : true,
            additionalData: tokenSettings.additionalData,
        });
    }
    return tokens;
}


export function decodeCurveData(
    program: Program<FundsIDL>,
    curveDataAccountInfo: AccountInfo<Buffer>,
): CurveChainData {
    let state = program.coder.accounts.decode("prismData", curveDataAccountInfo.data);
    return state;
}

export function decodeFunds(
    program: Program<FundsIDL>,
    fundStateAccountInfos: GetProgramAccountsResponse,
): {pubkey: PublicKey, fund: FundStateChainData}[] {
    return fundStateAccountInfos.map(account => { return {
        pubkey: account.pubkey,
        fund: program.coder.accounts.decode("fundState", account.account.data)
    }});
}

export function decodeOraclePrices(
    oracleDataAccountInfos: Array<AccountInfo<Buffer>>,
    tokenListData: TokenSettings[],
): OraclePrice[] {
    let oraclePrices = [];
    for(let i=0; i<oracleDataAccountInfos.length; i++){
        let avgPrice = 0;
        let baseConfidence = 0;
        if(oracleDataAccountInfos[i] == null) {} else
        if (tokenListData[i].oracleType == "Pyth") {
            let parsed = parsePriceData(oracleDataAccountInfos[i].data).aggregate;
            avgPrice = parsed.price * 10 ** 12;
            baseConfidence = parsed.confidence * 10 ** 12 * tokenListData[i].fixedConfidenceBps / 100;
        } else {
            let price = parseInt(new BN(
                oracleDataAccountInfos[i].data.subarray(
                    tokenListData[i].oracleIndex * 8 + 9,
                    tokenListData[i].oracleIndex * 8 + 17
                ),
                10,
                "le"
            ).toString());
            baseConfidence = price * tokenListData[i].oracleConfidencePct / 10000;
            avgPrice = price - baseConfidence;
        }
        let additionalConfidence = avgPrice * tokenListData[i].fixedConfidenceBps / 10000;
        oraclePrices.push({
            sell_price: avgPrice - baseConfidence - additionalConfidence,
            avg_price: avgPrice,
            buy_price: avgPrice + baseConfidence + additionalConfidence,
        })
    }
    return oraclePrices;
}

