import 'dotenv/config';
import 'reflect-metadata';
import { initializeDatabase } from '../infrastructure/persistence/data-source';
import { redisClient } from '../infrastructure/cache/redis-client';
import { createRepositories } from '../main/factories/repositories.factory';
import { createServices } from '../main/factories/services.factory';
import { Token } from '../domain/entities/token';

const run = async (): Promise<void> => {
  await initializeDatabase();

  const repositories = createRepositories();
  const services = createServices(redisClient);
  const { logger, coinMarketCapClient } = services;

  const [tokens, networks] = await Promise.all([
    repositories.tokenRepository.findAll(),
    repositories.networkRepository.findAll(),
  ]);

  const networkById = new Map(networks.map((network) => [network.id, network]));

  logger.info('Starting token nativity healing', {
    totalTokens: tokens.length,
    totalNetworks: networks.length,
  });

  const tokensWithNetwork = tokens.map((token) => {
    const network = networkById.get(token.networkId);
    return {
      id: token.id,
      name: token.name,
      symbol: token.symbol,
      address: token.address,
      networkName: network?.name ?? token.networkId,
      token,
    };
  });

  const coinEntries = await coinMarketCapClient.getTokenCoinIdsByNetwork(tokensWithNetwork);

  let healed = 0;
  let unchanged = 0;
  let undetermined = 0;

  for (const { token } of tokensWithNetwork) {
    const entry = coinEntries.get(token.id);
    if (!entry) {
      undetermined++;
      continue;
    }

    const isNative = entry.platform === null || entry.slug.toLowerCase() === entry.platform.slug.toLowerCase();
    if (isNative === token.isNative) {
      unchanged++;
      continue;
    }

    const updatedMcap = isNative ? token.mcap : 0;
    const updated = new Token(
      token.id,
      isNative,
      token.address,
      token.networkId,
      token.thumbnail,
      token.name,
      token.symbol,
      token.decimals,
      token.code,
      token.info,
      token.isActive,
      token.minimumSwapValue,
      updatedMcap,
      token.createdAt,
      new Date(),
    );
    await repositories.tokenRepository.update(updated);
    healed++;

    logger.info('Healed token nativity', {
      id: token.id,
      symbol: token.symbol,
      networkName: networkById.get(token.networkId)?.name ?? token.networkId,
      wasNative: token.isNative,
      isNative,
      cmcId: entry.id,
      cmcSlug: entry.slug,
      platformSlug: entry.platform?.slug ?? null,
      mcap: updatedMcap,
    });
  }

  logger.info('Token nativity healing complete', {
    totalTokens: tokens.length,
    healed,
    unchanged,
    undetermined,
  });

  await redisClient.quit();
  process.exit(0);
};

run().catch((error) => {
  // eslint-disable-next-line no-console
  console.error('Token nativity healing failed', error);
  process.exit(1);
});
