import { Network } from '../domain/entities/network';
import { ITokenRepository } from '../domain/repositories/token-repository';
import { UseCases } from '../main/factories/use-cases.factory';
import { ILogger } from '../application/ports/logger';
import { CoinMarketCapClient, CoinMarketCapMapEntry } from '../infrastructure/external/coinmarketcap-client';
import { getNativeDecimals } from './networks.seed-data';

const buildTokenThumbnail = (entry: CoinMarketCapMapEntry): string => {
  const symbol = entry.symbol.toLowerCase();
  return `https://cryptologos.cc/logos/${entry.slug}-${symbol}-logo.png`;
};

const buildCreateTokenInput = (
  entry: CoinMarketCapMapEntry,
  network: Network,
): Parameters<UseCases['createToken']['execute']>[0] => {
  const isNative = entry.platform === null;
  const address = isNative ? null : entry.platform?.token_address ?? null;
  const decimals = isNative ? getNativeDecimals(network.addressType, network.code) : 18;

  return {
    isNative,
    address,
    networkId: network.id,
    thumbnail: buildTokenThumbnail(entry),
    name: entry.name,
    symbol: entry.symbol,
    decimals,
    code: entry.slug,
    cmcId: entry.id,
    isActive: true,
  };
};

export const seedTokensForNetwork = async (
  network: Network,
  useCases: UseCases,
  tokenRepository: ITokenRepository,
  coinMarketCapClient: CoinMarketCapClient,
  logger: ILogger,
  limit = 20,
): Promise<{ fetched: number; created: number; skipped: number }> => {
  const entries = await coinMarketCapClient.getTopTokensByNetwork(
    { name: network.name, symbol: network.code },
    limit,
  );

  const existingTokens = await tokenRepository.findByNetworkId(network.id);
  const existingCodes = new Set(existingTokens.map((token) => token.code));

  let created = 0;
  let skipped = 0;

  for (const entry of entries) {
    if (existingCodes.has(entry.slug)) {
      logger.info('Token already exists, skipping', {
        network: network.name,
        token: entry.name,
        code: entry.slug,
      });
      skipped++;
      continue;
    }

    try {
      await useCases.createToken.execute(buildCreateTokenInput(entry, network));
      logger.info('Token created', { network: network.name, token: entry.name, code: entry.slug });
      created++;
    } catch (error) {
      logger.error('Failed to create token', {
        network: network.name,
        token: entry.name,
        code: entry.slug,
        error,
      });
    }
  }

  return { fetched: entries.length, created, skipped };
};
