import { readFile, writeFile } from 'fs/promises';
import { getCoingekoLists } from './getCoingeko';
import { getTopTokens } from './getTopTokens';
import type { TokenInfo, TokenList } from './types';

const fileHeader = `/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type { TokenList } from '@uniswap/token-lists';

export const tokenlist: TokenList = `;

const logoURI = 'https://cdn.jsdelivr.net/gh/cpuchain/coingecko-tokens@main/cg.png';

export async function getPinnedList() {
    return JSON.parse(await readFile('./pinnedTokens.json', { encoding: 'utf8' })) as TokenInfo[];
}

async function main() {
    const lists = await getCoingekoLists({ rebuildList: true });
    const pinnedTokens = await getPinnedList();
    const { topTokens, topTokenSet } = await getTopTokens();

    const tokenList = lists[0];

    lists.forEach((list) => {
        tokenList.tokens.push(...list.tokens);
    });

    const chains = [...new Set(tokenList.tokens.map(({ chainId }) => chainId))];

    console.log(`Got ${tokenList.tokens.length} tokens of ${chains.join(', ')} chains from coingecko`);

    /**
     * Init top 1000 tokens
     */
    const newTokenList = JSON.parse(JSON.stringify(tokenList)) as TokenList;

    /**
     * Fix broken logo file
     */
    (newTokenList as { name: string }).name = 'CoinGecko Top 1000';
    (newTokenList as { logoURI: string }).logoURI = logoURI;

    const uniqueSet = new Set();

    newTokenList.tokens = pinnedTokens
        .concat(newTokenList.tokens)
        .filter((token) => {
            const isPinnedOrTop = token.extensions?.pinned || topTokenSet.has(token.symbol?.toLowerCase());

            if (!isPinnedOrTop) {
                return false;
            }

            const key = `${token.chainId}_${token.address}`;

            if (uniqueSet.has(key)) {
                return false;
            }

            uniqueSet.add(key);
            return true;
        })
        .map((token) => {
            const { current_price, market_cap_rank, name } =
                topTokens.find((tkn) => tkn.symbol === token.symbol.toLowerCase()) || {};

            const isPinned = token.extensions?.pinned;

            const isBinancePeg = token.name.startsWith('Binance Peg');

            const isSameName = token.name === name;

            if (current_price && market_cap_rank && (isPinned || isBinancePeg || isSameName)) {
                token.extensions = {
                    ...(token.extensions || {}),
                    marketCapRank: market_cap_rank,
                    usdPrice: current_price,
                };
            }

            return token;
        })
        .sort((a, b) => {
            if (a.chainId === b.chainId) {
                const aRank = Number(a.extensions?.marketCapRank || newTokenList.tokens.length);
                const bRank = Number(b.extensions?.marketCapRank || newTokenList.tokens.length);

                return aRank - bRank;
            }
            return a.chainId - b.chainId;
        });

    console.log(
        `Got ${newTokenList.tokens.length} tokens of ${chains.join(', ')} chains from coingecko top 1000`,
    );

    await writeFile('./tokenlist.json', JSON.stringify(tokenList, null, 2));

    await writeFile('./tokenlist.top.json', JSON.stringify(newTokenList, null, 2));

    await writeFile('./src/tokenlist.ts', `${fileHeader}${JSON.stringify(tokenList, null, 2)};`);

    await writeFile('./src/top.ts', `${fileHeader}${JSON.stringify(newTokenList, null, 2)};`);
}

main();
