import { ethers } from 'ethers';

export const PAYMENT_LEAF_V1 = 0x01;
export const PAYMENT_LEAF_V2 = 0x02;

export type PaymentLeafV1Input = {
  txHash: string;
  from: string;
  to: string;
  /** Wei used in Merkle / on-chain leaf (native or effective token amount). */
  value: bigint | string | number;
  blockNumber: bigint | string | number;
  blockTimestamp: bigint | string | number;
  gasUsed: bigint | string | number;
  success: boolean;
};

export type PaymentLeafV2Input = PaymentLeafV1Input & {
  token: string;
  logIndex?: bigint | string | number;
};

/** Matches CheckpointLeaf.paymentLeafV1 */
export function paymentLeafV1Hash(chainId: number | bigint, leaf: PaymentLeafV1Input): string {
  const version = ethers.hexlify(new Uint8Array([PAYMENT_LEAF_V1]));
  return ethers.keccak256(
    ethers.AbiCoder.defaultAbiCoder().encode(
      ['bytes1', 'uint64', 'bytes32', 'address', 'address', 'uint256', 'uint256', 'uint64', 'uint256', 'bool'],
      [
        version,
        BigInt(chainId),
        leaf.txHash,
        leaf.from,
        leaf.to,
        BigInt(leaf.value ?? 0),
        BigInt(leaf.blockNumber),
        BigInt(leaf.blockTimestamp),
        BigInt(leaf.gasUsed ?? 0),
        leaf.success,
      ]
    )
  );
}

/** Matches CheckpointLeaf.paymentLeafV2 */
export function paymentLeafV2Hash(chainId: number | bigint, leaf: PaymentLeafV2Input): string {
  const version = ethers.hexlify(new Uint8Array([PAYMENT_LEAF_V2]));
  return ethers.keccak256(
    ethers.AbiCoder.defaultAbiCoder().encode(
      [
        'bytes1',
        'uint64',
        'bytes32',
        'address',
        'address',
        'address',
        'uint256',
        'uint256',
        'uint64',
        'uint256',
        'bool',
        'uint32',
      ],
      [
        version,
        BigInt(chainId),
        leaf.txHash,
        leaf.from,
        leaf.to,
        leaf.token,
        BigInt(leaf.value ?? 0),
        BigInt(leaf.blockNumber),
        BigInt(leaf.blockTimestamp),
        BigInt(leaf.gasUsed ?? 0),
        leaf.success,
        Number(leaf.logIndex ?? 0),
      ]
    )
  );
}

/** Historical batches 1–50: verify with native/on-chain wei only. */
export function merkleVerifyValueWei(record: Record<string, unknown>): bigint {
  if (record.onChainValueWei != null && String(record.onChainValueWei) !== '') {
    return BigInt(String(record.onChainValueWei));
  }
  return BigInt(String(record.nativeValueWei ?? record.value ?? '0'));
}

export function effectiveTokenOrNativeWei(record: {
  value?: unknown;
  token?: unknown;
  tokenValue?: unknown;
}): bigint {
  const native = BigInt(String(record.value ?? '0'));
  const tokenVal =
    record.token != null && record.tokenValue != null ? BigInt(String(record.tokenValue)) : 0n;
  if (tokenVal > 0n) return tokenVal;
  return native;
}
