const {
  BASIS_POINTS,
  TOKEN_DISTRIBUTION,
  TOKEN_DISTRIBUTION_POLICY
} = require('../config/tokenDistributionConfig');
const { EVM_CANDIDATE_CONFIG } = require('../config/evmCandidateConfig');

function tokenAmountFromBps(bps) {
  return (BigInt(TOKEN_DISTRIBUTION_POLICY.supply) * BigInt(bps)) / BigInt(BASIS_POINTS);
}

function unlockedAtMonth(category, month) {
  const allocation = tokenAmountFromBps(category.allocationBps);
  const initial = tokenAmountFromBps(category.tgeUnlockBps);
  if (month <= category.cliffMonths) return initial;
  const elapsed = Math.min(month - category.cliffMonths, category.vestingMonths);
  if (elapsed >= category.vestingMonths) return allocation;
  return initial + ((allocation - initial) * BigInt(elapsed)) / BigInt(category.vestingMonths);
}

function formatTokens(value) {
  return Number(value).toLocaleString('en-US', { maximumFractionDigits: 0 });
}

function formatPercent(value) {
  const scaled = (value * 1000000n) / BigInt(TOKEN_DISTRIBUTION_POLICY.supply);
  const whole = scaled / 10000n;
  const fraction = String(scaled % 10000n).padStart(4, '0');
  return `${whole}.${fraction}`;
}

function assertPolicy() {
  const allocationTotal = TOKEN_DISTRIBUTION.reduce((sum, row) => sum + row.allocationBps, 0);
  const tgeTotal = TOKEN_DISTRIBUTION.reduce((sum, row) => sum + row.tgeUnlockBps, 0);
  const ids = new Set(TOKEN_DISTRIBUTION.map((row) => row.id));

  if (allocationTotal !== BASIS_POINTS) throw new Error(`Allocation total must be ${BASIS_POINTS} bps; got ${allocationTotal}.`);
  if (tgeTotal !== TOKEN_DISTRIBUTION_POLICY.tgeMaximumCirculationBps) throw new Error(`TGE total mismatch: ${tgeTotal} bps.`);
  if (ids.size !== TOKEN_DISTRIBUTION.length) throw new Error('Distribution category ids must be unique.');
  if (String(EVM_CANDIDATE_CONFIG.genesisSupply) !== TOKEN_DISTRIBUTION_POLICY.supply) throw new Error('Distribution supply does not match canonical genesis supply.');
  if (Number(EVM_CANDIDATE_CONFIG.currencyDecimals) !== TOKEN_DISTRIBUTION_POLICY.decimals) throw new Error('Distribution decimals do not match the canonical asset.');
  if (TOKEN_DISTRIBUTION_POLICY.publicIssuanceApproved !== false) throw new Error('Public issuance must remain locked.');
  if (TOKEN_DISTRIBUTION_POLICY.externalTransfersEnabled !== EVM_CANDIDATE_CONFIG.externalTransfersEnabled) {
    throw new Error('Distribution transfer policy does not match the canonical EVM runtime.');
  }

  for (const row of TOKEN_DISTRIBUTION) {
    if (row.tgeUnlockBps > row.allocationBps) throw new Error(`${row.id} TGE unlock exceeds its allocation.`);
    if (row.vestingMonths <= 0) throw new Error(`${row.id} must define a positive vesting duration.`);
    if (unlockedAtMonth(row, 0) > unlockedAtMonth(row, 120)) throw new Error(`${row.id} unlock schedule is not monotonic.`);
    if (unlockedAtMonth(row, row.cliffMonths + row.vestingMonths) !== tokenAmountFromBps(row.allocationBps)) {
      throw new Error(`${row.id} does not fully unlock at the end of its schedule.`);
    }
  }
}

function buildSummary() {
  const checkpoints = [0, 6, 12, 24, 36, 48, 60, 72, 120];
  return checkpoints.map((month) => {
    const unlocked = TOKEN_DISTRIBUTION.reduce((sum, row) => sum + unlockedAtMonth(row, month), 0n);
    return {
      month,
      maximumUnlockedTokens: unlocked.toString(),
      maximumUnlockedDisplay: formatTokens(unlocked),
      maximumUnlockedPercent: formatPercent(unlocked),
      maximumUnlockedBpsFloor: Number((unlocked * 10000n) / BigInt(TOKEN_DISTRIBUTION_POLICY.supply))
    };
  });
}

assertPolicy();

console.log(JSON.stringify({
  ok: true,
  version: TOKEN_DISTRIBUTION_POLICY.version,
  supply: TOKEN_DISTRIBUTION_POLICY.supply,
  tgeMaximumCirculationBps: TOKEN_DISTRIBUTION_POLICY.tgeMaximumCirculationBps,
  categories: TOKEN_DISTRIBUTION.map((row) => ({
    ...row,
    allocationTokens: tokenAmountFromBps(row.allocationBps).toString(),
    tgeUnlockTokens: tokenAmountFromBps(row.tgeUnlockBps).toString()
  })),
  checkpoints: buildSummary()
}, null, 2));
