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

export interface DeploymentStatusFile {
  version?: string;
  updated?: string;
  chains?: Record<
    string,
    {
      name?: string;
      cwTokens?: Record<string, string>;
      gasMirrors?: Record<string, string>;
      gasQuoteAddresses?: Record<string, string>;
      gasPmmPools?: Array<Record<string, unknown>>;
      gasReferenceVenues?: Array<Record<string, unknown>>;
      [key: string]: unknown;
    }
  >;
  [key: string]: unknown;
}

export interface LoadedDeploymentStatus {
  data: DeploymentStatusFile;
  lastModified?: string;
}

export interface CwRegistryChain {
  chainId: number;
  chainIdText: string;
  name: string;
  tokens: Array<{
    symbol: string;
    address: string;
    assetClass?: string;
    familyKey?: string;
  }>;
}

export interface GasRegistryChain {
  chainId: number;
  chainIdText: string;
  name: string;
  families: Array<{
    familyKey: string;
    mirroredSymbol: string;
    mirrorAddress?: string;
    dodoPmm: Array<Record<string, unknown>>;
    referenceVenues: Array<Record<string, unknown>>;
  }>;
}

function uniquePaths(paths: Array<string | undefined | null>): string[] {
  const seen = new Set<string>();
  const out: string[] = [];

  for (const candidate of paths) {
    if (typeof candidate !== 'string') continue;
    const trimmed = candidate.trim();
    if (!trimmed || seen.has(trimmed)) continue;
    seen.add(trimmed);
    out.push(trimmed);
  }

  return out;
}

function buildDeploymentStatusCandidates(): string[] {
  return uniquePaths([
    process.env.DEPLOYMENT_STATUS_JSON_PATH,
    process.env.CW_REGISTRY_JSON_PATH,
    process.env.CROSS_CHAIN_PMM_DEPLOYMENT_STATUS_PATH,
    path.resolve(process.cwd(), 'cross-chain-pmm-lps/config/deployment-status.json'),
    path.resolve(process.cwd(), '..', 'cross-chain-pmm-lps/config/deployment-status.json'),
    path.resolve(process.cwd(), '..', '..', 'cross-chain-pmm-lps/config/deployment-status.json'),
    path.resolve(__dirname, '../../../../../cross-chain-pmm-lps/config/deployment-status.json'),
  ]);
}

export function resolveDeploymentStatusPath(): string | null {
  for (const candidate of buildDeploymentStatusCandidates()) {
    if (fs.existsSync(candidate)) {
      return candidate;
    }
  }
  return null;
}

export function loadDeploymentStatusFile(): LoadedDeploymentStatus | null {
  const filePath = resolveDeploymentStatusPath();
  if (!filePath) return null;

  try {
    const raw = fs.readFileSync(filePath, 'utf8');
    const stat = fs.statSync(filePath);
    return {
      data: JSON.parse(raw) as DeploymentStatusFile,
      lastModified: stat.mtime.toISOString(),
    };
  } catch {
    return null;
  }
}

export function buildCwRegistryChains(data: DeploymentStatusFile): CwRegistryChain[] {
  const chains = data.chains ?? {};
  const rows: CwRegistryChain[] = [];

  for (const [chainIdText, chain] of Object.entries(chains)) {
    const gasFamilyByMirror = new Map<string, string>();
    for (const pool of chain.gasPmmPools ?? []) {
      const familyKey = typeof pool.familyKey === 'string' ? pool.familyKey : '';
      const base = typeof pool.base === 'string' ? pool.base : '';
      if (familyKey && base) {
        gasFamilyByMirror.set(base, familyKey);
      }
    }

    const tokens = [
      ...Object.entries(chain.cwTokens ?? {})
        .filter(([, address]) => typeof address === 'string' && address.trim() !== '')
        .map(([symbol, address]) => ({ symbol, address })),
      ...Object.entries(chain.gasMirrors ?? {})
        .filter(([, address]) => typeof address === 'string' && address.trim() !== '')
        .map(([symbol, address]) => ({
          symbol,
          address,
          assetClass: 'gas_native',
          familyKey: gasFamilyByMirror.get(symbol),
        })),
    ];

    if (tokens.length === 0) continue;

    rows.push({
      chainId: Number(chainIdText),
      chainIdText,
      name: chain.name || `Chain ${chainIdText}`,
      tokens: tokens.sort((a, b) => a.symbol.localeCompare(b.symbol)),
    });
  }

  return rows.sort((a, b) => a.chainId - b.chainId);
}

export function buildGasRegistryChains(data: DeploymentStatusFile): GasRegistryChain[] {
  const rows: GasRegistryChain[] = [];

  for (const [chainIdText, chain] of Object.entries(data.chains ?? {})) {
    const familyMap = new Map<
      string,
      {
        familyKey: string;
        mirroredSymbol: string;
        mirrorAddress?: string;
        dodoPmm: Array<Record<string, unknown>>;
        referenceVenues: Array<Record<string, unknown>>;
      }
    >();

    for (const pool of chain.gasPmmPools ?? []) {
      const familyKey = typeof pool.familyKey === 'string' ? pool.familyKey : '';
      const mirroredSymbol = typeof pool.base === 'string' ? pool.base : '';
      if (!familyKey || !mirroredSymbol) continue;
      const existing = familyMap.get(familyKey) ?? {
        familyKey,
        mirroredSymbol,
        mirrorAddress: chain.gasMirrors?.[mirroredSymbol],
        dodoPmm: [],
        referenceVenues: [],
      };
      existing.dodoPmm.push(pool);
      familyMap.set(familyKey, existing);
    }

    for (const venue of chain.gasReferenceVenues ?? []) {
      const familyKey = typeof venue.familyKey === 'string' ? venue.familyKey : '';
      if (!familyKey) continue;
      const existing = familyMap.get(familyKey) ?? {
        familyKey,
        mirroredSymbol: typeof venue.base === 'string' ? venue.base : '',
        mirrorAddress: typeof venue.base === 'string' ? chain.gasMirrors?.[venue.base] : undefined,
        dodoPmm: [],
        referenceVenues: [],
      };
      existing.referenceVenues.push(venue);
      familyMap.set(familyKey, existing);
    }

    const families = Array.from(familyMap.values()).sort((a, b) => a.familyKey.localeCompare(b.familyKey));
    if (families.length === 0) continue;

    rows.push({
      chainId: Number(chainIdText),
      chainIdText,
      name: chain.name || `Chain ${chainIdText}`,
      families,
    });
  }

  return rows.sort((a, b) => a.chainId - b.chainId);
}
