import { ethers } from 'ethers';

/** Matches CheckpointLeaf.buildMerkleRoot (pair sort, odd duplicate). */
export function buildMerkleRoot(hashes: string[]): string {
  if (hashes.length === 0) return ethers.ZeroHash;
  let layer = [...hashes];
  while (layer.length > 1) {
    const next: string[] = [];
    for (let i = 0; i < layer.length; i += 2) {
      const a = layer[i];
      const b = i + 1 < layer.length ? layer[i + 1] : a;
      const [left, right] = a.toLowerCase() < b.toLowerCase() ? [a, b] : [b, a];
      next.push(ethers.keccak256(ethers.concat([left, right])));
    }
    layer = next;
  }
  return layer[0];
}

export function merkleProofs(hashes: string[]): { root: string; proofs: string[][] } {
  const root = buildMerkleRoot(hashes);
  const proofs = hashes.map((_, index) => buildProof(hashes, index));
  return { root, proofs };
}

function buildProof(leaves: string[], index: number): string[] {
  const proof: string[] = [];
  let idx = index;
  let layer = [...leaves];
  while (layer.length > 1) {
    const next: string[] = [];
    for (let i = 0; i < layer.length; i += 2) {
      const a = layer[i];
      const b = i + 1 < layer.length ? layer[i + 1] : a;
      const [left, right] = a.toLowerCase() < b.toLowerCase() ? [a, b] : [b, a];
      next.push(ethers.keccak256(ethers.concat([left, right])));
    }
    const siblingIdx = idx % 2 === 0 ? idx + 1 : idx - 1;
    if (siblingIdx < layer.length) proof.push(layer[siblingIdx]);
    idx = Math.floor(idx / 2);
    layer = next;
  }
  return proof;
}
