import fs from 'fs';
import path from 'path';

type DiscoveryPayload = {
  generated_at?: string;
  entries?: Array<{
    chain_id: number;
    network?: string;
    factoryAddress?: string;
    routerAddress?: string;
    pairsChecked?: Array<{
      base: string;
      quote: string;
      poolAddress: string;
      live: boolean;
      health?: {
        baseReserveRaw?: string;
        quoteReserveRaw?: string;
        baseReserveUnits?: string;
        quoteReserveUnits?: string;
        priceQuotePerBase?: string;
        deviationBps?: string;
        healthy?: boolean;
        depthOk?: boolean;
        parityOk?: boolean;
      };
    }>;
  }>;
};

type DeploymentStatusChain = {
    name?: string;
    cwTokens?: Record<string, string>;
    anchorAddresses?: Record<string, string>;
    gasMirrors?: Record<string, string>;
    gasQuoteAddresses?: Record<string, string>;
};

type DeploymentStatus = {
  chains?: Record<string, DeploymentStatusChain>;
};

const PRICE_USD: Record<string, number> = {
  USDC: 1,
  USDT: 1,
  cUSDC: 1,
  cUSDT: 1,
  cWUSDC: 1,
  cWUSDT: 1,
  cWAUSDT: 1,
  BTC: Number(process.env.CANONICAL_PRICE_USD_BTC || process.env.BTC_PRICE_USD || 90000),
  cWBTC: Number(process.env.CANONICAL_PRICE_USD_BTC || process.env.BTC_PRICE_USD || 90000),
};

function findRepoRoot(): string {
  const candidates = [
    process.env.PROXMOX_REPO_ROOT,
    process.env.PROJECT_ROOT,
    path.resolve(process.cwd(), '..', '..', '..'),
    path.resolve(process.cwd(), '..'),
    process.cwd(),
  ].filter(Boolean) as string[];

  for (const candidate of candidates) {
    if (fs.existsSync(path.join(candidate, 'reports/extraction/promod-uniswap-v2-live-pair-discovery-latest.json'))) {
      return candidate;
    }
  }
  return path.resolve(process.cwd(), '..', '..', '..');
}

function readJson<T>(filePath: string): T {
  return JSON.parse(fs.readFileSync(filePath, 'utf8')) as T;
}

function resolveTokenAddress(chain: DeploymentStatusChain, symbol: string): string | undefined {
  return (
    chain.cwTokens?.[symbol] ||
    chain.anchorAddresses?.[symbol] ||
    chain.gasMirrors?.[symbol] ||
    chain.gasQuoteAddresses?.[symbol]
  );
}

function parsePositiveNumber(value?: string): number {
  const parsed = Number(value);
  return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
}

type CwbtcPoolHealth = {
  id?: string;
  pair?: string;
  venue?: string;
  poolAddress?: string;
  live?: boolean;
  baseReserveRaw?: string;
  quoteReserveRaw?: string;
  errors?: string[];
};

type CwbtcPoolsHealthPayload = {
  chainId?: number;
  tokenAddress?: string;
  token?: { symbol?: string; addressDefault?: string; decimals?: number };
  quoteTokens?: Record<string, string>;
  pools?: CwbtcPoolHealth[];
};

function mergeCwbtcMeshPools(repoRoot: string, pools: Array<Record<string, unknown>>): void {
  const healthPath = path.join(repoRoot, 'reports/status/mainnet-cwbtc-pools-health-latest.json');
  const poolsConfigPath = path.join(repoRoot, 'config/engine/mainnet-cwbtc-pools.v1.json');
  if (!fs.existsSync(healthPath) || !fs.existsSync(poolsConfigPath)) return;

  const health = readJson<CwbtcPoolsHealthPayload>(healthPath);
  const poolsConfig = readJson<{ quoteTokens?: Record<string, string> }>(poolsConfigPath);
  const chainId = Number(health.chainId ?? 1);
  const cwbtcAddress = String(health.tokenAddress ?? health.token?.addressDefault ?? '').toLowerCase();
  const quoteTokens = { ...(poolsConfig.quoteTokens ?? {}), ...(health.quoteTokens ?? {}) };
  const existing = new Set(pools.map((pool) => String(pool.poolAddress).toLowerCase()));

  for (const row of health.pools ?? []) {
    if (!row.live || row.venue !== 'uniswap_v2') continue;
    const poolAddress = String(row.poolAddress ?? '').toLowerCase();
    if (!poolAddress.startsWith('0x') || existing.has(poolAddress)) continue;

    const [baseSymbol, quoteSymbol] = String(row.pair ?? '/').split('/');
    const baseAddress =
      baseSymbol === 'cWBTC' ? cwbtcAddress : String(quoteTokens[baseSymbol] ?? '').toLowerCase();
    const quoteAddress =
      quoteSymbol === 'cWBTC' ? cwbtcAddress : String(quoteTokens[quoteSymbol] ?? '').toLowerCase();
    if (!baseAddress.startsWith('0x') || !quoteAddress.startsWith('0x')) continue;

    const baseDecimals = baseSymbol === 'cWBTC' ? 8 : 6;
    const quoteDecimals = quoteSymbol === 'cWBTC' ? 8 : 6;
    const baseReserveRaw = parsePositiveNumber(row.baseReserveRaw);
    const quoteReserveRaw = parsePositiveNumber(row.quoteReserveRaw);
    if (baseReserveRaw <= 0 && quoteReserveRaw <= 0) continue;

    const baseReserveUnits = baseReserveRaw / 10 ** baseDecimals;
    const quoteReserveUnits = quoteReserveRaw / 10 ** quoteDecimals;
    const basePriceUsd = PRICE_USD[baseSymbol] ?? 0;
    const quotePriceUsd = PRICE_USD[quoteSymbol] ?? 0;
    const reserve0Usd = baseReserveUnits * basePriceUsd;
    const reserve1Usd = quoteReserveUnits * quotePriceUsd;
    const totalLiquidityUsd = reserve0Usd + reserve1Usd;
    if (totalLiquidityUsd <= 0) continue;

    pools.push({
      chainId,
      chainName: 'Ethereum Mainnet',
      poolAddress,
      dex: 'uniswap_v2',
      baseSymbol,
      quoteSymbol,
      baseAddress,
      quoteAddress,
      baseReserveRaw: String(row.baseReserveRaw ?? ''),
      quoteReserveRaw: String(row.quoteReserveRaw ?? ''),
      baseReserveUnits: String(baseReserveUnits),
      quoteReserveUnits: String(quoteReserveUnits),
      healthy: true,
      depthOk: true,
      parityOk: true,
      reserve0Usd,
      reserve1Usd,
      totalLiquidityUsd,
      sourceArtifact: 'reports/status/mainnet-cwbtc-pools-health-latest.json',
    });
    existing.add(poolAddress);
  }
}

function main(): void {
  const repoRoot = findRepoRoot();
  const discoveryPath = path.join(repoRoot, 'reports/extraction/promod-uniswap-v2-live-pair-discovery-latest.json');
  const deploymentStatusPath = path.join(repoRoot, 'cross-chain-pmm-lps/config/deployment-status.json');
  const outPath = path.join(process.cwd(), 'config/live-uniswap-v2-pool-catalog.json');

  const pools: Array<Record<string, unknown>> = [];

  if (fs.existsSync(discoveryPath) && fs.existsSync(deploymentStatusPath)) {
    const discovery = readJson<DiscoveryPayload>(discoveryPath);
    const deploymentStatus = readJson<DeploymentStatus>(deploymentStatusPath);

    for (const entry of discovery.entries ?? []) {
      const chainId = Number(entry.chain_id);
      const chain = deploymentStatus.chains?.[String(chainId)];
      if (!chain) continue;

      for (const pair of entry.pairsChecked ?? []) {
        if (!pair.live || !pair.poolAddress?.startsWith('0x') || !pair.health) continue;
        const baseAddress = resolveTokenAddress(chain, pair.base);
        const quoteAddress = resolveTokenAddress(chain, pair.quote);
        if (!baseAddress || !quoteAddress) continue;

        const baseReserveUnits = parsePositiveNumber(pair.health.baseReserveUnits);
        const quoteReserveUnits = parsePositiveNumber(pair.health.quoteReserveUnits);
        const basePriceUsd = PRICE_USD[pair.base] ?? 0;
        const quotePriceUsd = PRICE_USD[pair.quote] ?? 0;
        const reserve0Usd = baseReserveUnits * basePriceUsd;
        const reserve1Usd = quoteReserveUnits * quotePriceUsd;
        const totalLiquidityUsd = reserve0Usd + reserve1Usd;
        if (totalLiquidityUsd <= 0) continue;

        pools.push({
          chainId,
          chainName: entry.network ?? chain.name ?? `Chain ${chainId}`,
          poolAddress: pair.poolAddress.toLowerCase(),
          dex: 'uniswap_v2',
          factoryAddress: entry.factoryAddress,
          routerAddress: entry.routerAddress,
          baseSymbol: pair.base,
          quoteSymbol: pair.quote,
          baseAddress: baseAddress.toLowerCase(),
          quoteAddress: quoteAddress.toLowerCase(),
          baseReserveRaw: pair.health.baseReserveRaw,
          quoteReserveRaw: pair.health.quoteReserveRaw,
          baseReserveUnits: pair.health.baseReserveUnits,
          quoteReserveUnits: pair.health.quoteReserveUnits,
          priceQuotePerBase: pair.health.priceQuotePerBase,
          deviationBps: pair.health.deviationBps,
          depthOk: pair.health.depthOk,
          parityOk: pair.health.parityOk,
          healthy: pair.health.healthy,
          reserve0Usd,
          reserve1Usd,
          totalLiquidityUsd,
        });
      }
    }
  }

  mergeCwbtcMeshPools(repoRoot, pools);

  pools.sort(
    (a, b) =>
      Number(a.chainId) - Number(b.chainId) ||
      String(a.poolAddress).localeCompare(String(b.poolAddress))
  );
  const payload = {
    schema: 'token-aggregation-live-uniswap-v2-pool-catalog/v1',
    generatedAt: new Date().toISOString(),
    sourceArtifacts: [
      'reports/extraction/promod-uniswap-v2-live-pair-discovery-latest.json',
      'cross-chain-pmm-lps/config/deployment-status.json',
      'reports/status/mainnet-cwbtc-pools-health-latest.json',
    ],
    poolCount: pools.length,
    positiveLiquidityPoolCount: pools.filter((pool) => Number(pool.totalLiquidityUsd) > 0).length,
    pools,
  };

  fs.mkdirSync(path.dirname(outPath), { recursive: true });
  fs.writeFileSync(outPath, `${JSON.stringify(payload, null, 2)}\n`);
  console.log(outPath);
}

main();
