import * as fs from 'fs';
import * as path from 'path';

export type AddressActivityRow = {
  txHash: string;
  batchId: string;
  role: 'from' | 'to';
  counterparty: string;
  valueWei: string;
  valueUsd?: string;
  nativeValueUsd?: string;
  totalTransfersUsd?: string;
  blockNumber: number;
  blockTimestamp: number;
  logCount?: number;
  receiptHash?: string;
  success: boolean;
  transfers?: Array<Record<string, unknown>>;
};

export type AddressIndex = {
  builtAt: string;
  batchCount: number;
  txCount: number;
  byAddress: Record<string, AddressActivityRow[]>;
};

type LeafRecord = Record<string, unknown>;

function normalizeAddr(a: string): string {
  return a.toLowerCase();
}

function leafToRows(batchId: string, leaf: LeafRecord): AddressActivityRow[] {
  const txHash = String(leaf.txHash || '');
  const from = String(leaf.from || '');
  const to = String(leaf.to || '');
  const base = {
    txHash,
    batchId,
    valueWei: String(leaf.onChainValueWei ?? leaf.value ?? leaf.nativeValueWei ?? '0'),
    valueUsd: leaf.valueUsd != null ? String(leaf.valueUsd) : undefined,
    nativeValueUsd: leaf.nativeValueUsd != null ? String(leaf.nativeValueUsd) : undefined,
    totalTransfersUsd:
      leaf.totalTransfersUsd != null ? String(leaf.totalTransfersUsd) : undefined,
    blockNumber: Number(leaf.blockNumber ?? 0),
    blockTimestamp: Number(leaf.blockTimestamp ?? 0),
    logCount: leaf.logCount != null ? Number(leaf.logCount) : undefined,
    receiptHash: leaf.receiptHash != null ? String(leaf.receiptHash) : undefined,
    success: Boolean(leaf.success),
    transfers: Array.isArray(leaf.transfers)
      ? (leaf.transfers as Array<Record<string, unknown>>)
      : undefined,
  };
  const rows: AddressActivityRow[] = [];
  if (/^0x[a-fA-F0-9]{40}$/.test(from)) {
    rows.push({ ...base, role: 'from', counterparty: to });
  }
  if (/^0x[a-fA-F0-9]{40}$/.test(to)) {
    rows.push({ ...base, role: 'to', counterparty: from });
  }
  return rows;
}

export function buildAddressIndex(batchPayloadDir: string): AddressIndex {
  const byAddress: Record<string, AddressActivityRow[]> = {};
  let batchCount = 0;
  let txCount = 0;

  if (!fs.existsSync(batchPayloadDir)) {
    return { builtAt: new Date().toISOString(), batchCount: 0, txCount: 0, byAddress };
  }

  const files = fs
    .readdirSync(batchPayloadDir)
    .filter((f) => /^batch-\d+\.json$/.test(f))
    .sort((a, b) => {
      const na = parseInt(a.replace(/\D/g, ''), 10);
      const nb = parseInt(b.replace(/\D/g, ''), 10);
      return na - nb;
    });

  for (const file of files) {
    const batchId = file.replace('batch-', '').replace('.json', '');
    const raw = JSON.parse(fs.readFileSync(path.join(batchPayloadDir, file), 'utf8')) as {
      leaves?: LeafRecord[];
    };
    if (!raw.leaves?.length) continue;
    batchCount++;
    for (const leaf of raw.leaves) {
      txCount++;
      for (const row of leafToRows(batchId, leaf)) {
        const key = normalizeAddr(row.role === 'from' ? String(leaf.from) : String(leaf.to));
        if (!byAddress[key]) byAddress[key] = [];
        byAddress[key].push(row);
      }
    }
  }

  for (const key of Object.keys(byAddress)) {
    byAddress[key].sort((a, b) => b.blockNumber - a.blockNumber || b.batchId.localeCompare(a.batchId));
  }

  return {
    builtAt: new Date().toISOString(),
    batchCount,
    txCount,
    byAddress,
  };
}

export function getAddressTransactions(
  index: AddressIndex,
  address: string,
  limit = 50,
  offset = 0
): { total: number; items: AddressActivityRow[] } {
  const key = normalizeAddr(address);
  const all = index.byAddress[key] ?? [];
  return {
    total: all.length,
    items: all.slice(offset, offset + limit),
  };
}

export function summarizeAddressActivity(rows: AddressActivityRow[]): {
  txCount: number;
  totalValueWei: string;
  totalUsd: string;
} {
  let totalWei = 0n;
  let totalUsd = 0;
  const seen = new Set<string>();
  for (const row of rows) {
    if (seen.has(row.txHash)) continue;
    seen.add(row.txHash);
    try {
      totalWei += BigInt(row.valueWei);
    } catch {
      /* skip */
    }
    const usd = Number(row.totalTransfersUsd ?? row.valueUsd ?? '0');
    if (Number.isFinite(usd)) totalUsd += usd;
  }
  return {
    txCount: seen.size,
    totalValueWei: totalWei.toString(),
    totalUsd: totalUsd.toFixed(6),
  };
}
