/**
 * seed-generator.ts — Generate diverse seed variants for Push3 evolution (#638).
 *
 * Produces parametric Push3 programs that implement the same binary bear/bull
 * logic as optimizer_seed.push3, with systematically varied constants:
 *   - stakedThreshold:  the staked% cutoff for entering the penalty branch
 *   - penaltyThreshold: the penalty value below which bull params are used
 *   - bull/bear params: capitalInefficiency, anchorShare, anchorWidth, discoveryDepth
 *   - taxDistribution:  spacing of the 30-bucket tax-rate lookup thresholds
 *
 * All generated programs:
 *   1. Consume 8 inputs (same DYADIC stack interface as the seed)
 *   2. Produce 4 outputs in valid ranges [0..1e18] for fractions, positive int for anchorWidth
 *   3. Have identical structure to optimizer_seed.push3 (only constants differ)
 */

const ONE_E18 = 1_000_000_000_000_000_000n;

export interface BullBearParams {
  capitalInefficiency: bigint; // 0..1e18
  anchorShare: bigint;         // 0..1e18
  anchorWidth: number;         // tick units (positive integer)
  discoveryDepth: bigint;      // 0..1e18
}

export type TaxDistribution = 'exponential' | 'linear' | 'sqrt';

export interface SeedVariantParams {
  stakedThreshold: number;    // integer 0-100: staked% above which penalty branch is entered
  penaltyThreshold: number;   // penalty < N → bull output; else → bear output
  bull: BullBearParams;
  bear: BullBearParams;
  taxDistribution: TaxDistribution;
}

// ---- Parameter sets --------------------------------------------------------

/** Staked% thresholds: when staked > threshold, enter penalty computation. */
export const STAKED_THRESHOLDS: readonly number[] = [80, 85, 88, 91, 94, 97];

/** Penalty thresholds: penalty < N → bull; else → bear. */
export const PENALTY_THRESHOLDS: readonly number[] = [30, 50, 70, 100];

/**
 * Bull output parameter variants (higher confidence / tighter ranges).
 *
 * capitalInefficiency is intentionally fixed at 0 across all variants — the
 * current seed (optimizer_v3.push3) uses CI=0 in all branches, and varying it
 * is left to the mutation operators once evolution starts.
 */
export const BULL_VARIANTS: readonly BullBearParams[] = [
  // Aggressive bull (current seed): maximum confidence, tight anchor
  { capitalInefficiency: 0n, anchorShare: ONE_E18,                  anchorWidth: 20, discoveryDepth: ONE_E18 },
  // Tight bull: slightly lower confidence, narrower anchor
  { capitalInefficiency: 0n, anchorShare: 900_000_000_000_000_000n, anchorWidth: 15, discoveryDepth: 900_000_000_000_000_000n },
  // Moderate bull: wider anchor, moderate confidence
  { capitalInefficiency: 0n, anchorShare: 800_000_000_000_000_000n, anchorWidth: 30, discoveryDepth: 800_000_000_000_000_000n },
  // Mild bull: broad discovery, lower confidence
  { capitalInefficiency: 0n, anchorShare: 700_000_000_000_000_000n, anchorWidth: 50, discoveryDepth: 700_000_000_000_000_000n },
];

/**
 * Bear output parameter variants (defensive / wide ranges).
 *
 * capitalInefficiency is intentionally fixed at 0 for the same reason as BULL_VARIANTS.
 */
export const BEAR_VARIANTS: readonly BullBearParams[] = [
  // Standard bear (current seed)
  { capitalInefficiency: 0n, anchorShare: 300_000_000_000_000_000n, anchorWidth: 100, discoveryDepth: 300_000_000_000_000_000n },
  // Strong bear: very defensive, very wide anchor
  { capitalInefficiency: 0n, anchorShare: 200_000_000_000_000_000n, anchorWidth: 150, discoveryDepth: 200_000_000_000_000_000n },
  // Mild bear: slightly wider than neutral
  { capitalInefficiency: 0n, anchorShare: 400_000_000_000_000_000n, anchorWidth: 80,  discoveryDepth: 400_000_000_000_000_000n },
  // Very mild bear: approaching neutral
  { capitalInefficiency: 0n, anchorShare: 500_000_000_000_000_000n, anchorWidth: 60,  discoveryDepth: 500_000_000_000_000_000n },
];

export const TAX_DISTRIBUTIONS: readonly TaxDistribution[] = [
  'exponential',
  'linear',
  'sqrt',
];

// ---- Tax threshold distributions -------------------------------------------

/**
 * Current seed thresholds — exponentially spaced from ~2e14 to ~8.9e17.
 * These are the exact values from optimizer_seed.push3.
 */
function exponentialThresholds(): bigint[] {
  return [
    206185567010309n,
    412371134020618n,
    618556701030927n,
    1030927835051546n,
    1546391752577319n,
    2164948453608247n,
    2783505154639175n,
    3608247422680412n,
    4639175257731958n,
    5670103092783505n,
    7216494845360824n,
    9278350515463917n,
    11855670103092783n,
    15979381443298969n,
    22164948453608247n,
    29381443298969072n,
    38144329896907216n,
    49484536082474226n,
    63917525773195876n,
    83505154639175257n,
    109278350515463917n,
    144329896907216494n,
    185567010309278350n,
    237113402061855670n,
    309278350515463917n,
    402061855670103092n,
    520618556701030927n,
    680412371134020618n,
    886597938144329896n,
  ];
}

/**
 * Linear thresholds — 29 boundaries evenly spaced across [0, 1e18].
 * Gives equal bucket widths: each bucket covers 1/30 of the tax rate range.
 */
function linearThresholds(): bigint[] {
  return Array.from({ length: 29 }, (_, i) =>
    (BigInt(i + 1) * ONE_E18) / 30n,
  );
}

/**
 * Square-root thresholds — more buckets at low tax rates.
 * Threshold[i] = sqrt((i+1)/30) * 1e18.
 * Compresses the high-tax range and spreads the low-tax range.
 */
function sqrtThresholds(): bigint[] {
  return Array.from({ length: 29 }, (_, i) => {
    // Work in units of 1e9 for float precision, then scale with BigInt
    const scaled = Math.sqrt((i + 1) / 30) * 1e9;
    return BigInt(Math.round(scaled)) * 1_000_000_000n;
  });
}

/** Return the 29 boundary values for the given tax distribution. */
export function getTaxThresholds(dist: TaxDistribution): bigint[] {
  switch (dist) {
    case 'exponential': return exponentialThresholds();
    case 'linear':      return linearThresholds();
    case 'sqrt':        return sqrtThresholds();
  }
}

// ---- Code generation -------------------------------------------------------

/**
 * Generate the output-push fragment for a bear/bull param set.
 * Pushes 4 values bottom-first so stack top = CI, bottom = DD.
 */
function outputBlock(p: BullBearParams): string {
  return `${p.discoveryDepth} ${p.anchorWidth} ${p.anchorShare} ${p.capitalInefficiency}`;
}

/**
 * Generate the 30-way nested EXEC.IF tax-rate lookup.
 *
 * Returns inline Push3 tokens that, when placed inside a list, leave one
 * integer (0..29) on the DYADIC stack representing the tax rate bucket index.
 *
 * Structure (right-recursive):
 *   TAXRATE t0 DYADIC.<= EXEC.IF 0 ( TAXRATE t1 DYADIC.<= EXEC.IF 1 ( ... ) )
 */
function generateTaxLookup(thresholds: bigint[]): string {
  if (thresholds.length !== 29) {
    throw new Error(`Expected 29 tax thresholds, got ${thresholds.length}`);
  }

  function nested(i: number): string {
    if (i === 28) {
      // Innermost: threshold[28] → index 28 or 29
      return `TAXRATE ${thresholds[i]} DYADIC.<= EXEC.IF ${i} ${i + 1}`;
    }
    return `TAXRATE ${thresholds[i]} DYADIC.<= EXEC.IF ${i} ( ${nested(i + 1)} )`;
  }

  return nested(0);
}

/**
 * Generate Push3 source text for one seed variant.
 *
 * The generated program has the same structure as optimizer_seed.push3:
 *   1. Bind inputs 0-1; discard 2-7
 *   2. Scale percentageStaked → integer 0-100
 *   3. If staked > stakedThreshold: compute penalty, choose bull or bear
 *   4. Else: bear outputs
 */
export function generateSeedVariant(params: SeedVariantParams): string {
  const { stakedThreshold, penaltyThreshold, bull, bear, taxDistribution } = params;
  const thresholds = getTaxThresholds(taxDistribution);
  const taxLookup  = generateTaxLookup(thresholds);
  const bullOut    = outputBlock(bull);
  const bearOut    = outputBlock(bear);

  return [
    `;; Generated seed variant (#638)`,
    `;; staked_threshold=${stakedThreshold} penalty_threshold=${penaltyThreshold}`,
    `;; bull: CI=${bull.capitalInefficiency} AS=${bull.anchorShare} AW=${bull.anchorWidth} DD=${bull.discoveryDepth}`,
    `;; bear: CI=${bear.capitalInefficiency} AS=${bear.anchorShare} AW=${bear.anchorWidth} DD=${bear.discoveryDepth}`,
    `;; tax_distribution=${taxDistribution}`,
    `(`,
    `  PERCENTAGESTAKED DYADIC.DEFINE`,
    `  TAXRATE DYADIC.DEFINE`,
    `  DYADIC.POP`,
    `  DYADIC.POP`,
    `  DYADIC.POP`,
    `  DYADIC.POP`,
    `  DYADIC.POP`,
    `  DYADIC.POP`,
    `  PERCENTAGESTAKED`,
    `  100 DYADIC.*`,
    `  1000000000000000000 DYADIC./`,
    `  STAKED DYADIC.DEFINE`,
    `  STAKED ${stakedThreshold} DYADIC.>`,
    `  EXEC.IF`,
    `  (`,
    `    100 STAKED DYADIC.-`,
    `    DELTAS DYADIC.DEFINE`,
    `    ${taxLookup}`,
    `    DYADIC.DUP 14 DYADIC.>=`,
    `    EXEC.IF`,
    `      (`,
    `        1 DYADIC.+`,
    `        DYADIC.DUP 29 DYADIC.>`,
    `        EXEC.IF`,
    `          ( DYADIC.POP 29 )`,
    `          ( )`,
    `      )`,
    `      ( )`,
    `    EFFIDX DYADIC.DEFINE`,
    `    DELTAS DELTAS DYADIC.*`,
    `    DELTAS DYADIC.*`,
    `    EFFIDX DYADIC.*`,
    `    20 DYADIC./`,
    `    ${penaltyThreshold} DYADIC.<`,
    `    EXEC.IF`,
    `    ( ${bullOut} )`,
    `    ( ${bearOut} )`,
    `  )`,
    `  ( ${bearOut} )`,
    `)`,
  ].join('\n');
}

// ---- Variant enumeration ---------------------------------------------------

/**
 * Enumerate all parameter combinations in a deterministic order.
 *
 * STAKED_THRESHOLDS is the outermost loop so that even-stride sampling in
 * selectVariants(n) naturally covers all staked% values: each block of
 * (4 penalty × 4 bull × 4 bear × 3 tax) = 192 entries maps to one threshold.
 *
 * Total = |STAKED_THRESHOLDS| × |PENALTY_THRESHOLDS| × |BULL_VARIANTS|
 *           × |BEAR_VARIANTS| × |TAX_DISTRIBUTIONS|
 *       = 6 × 4 × 4 × 4 × 3 = 1152
 */
export function enumerateVariants(): SeedVariantParams[] {
  const result: SeedVariantParams[] = [];
  for (const stakedThreshold of STAKED_THRESHOLDS) {
    for (const penaltyThreshold of PENALTY_THRESHOLDS) {
      for (const bull of BULL_VARIANTS) {
        for (const bear of BEAR_VARIANTS) {
          for (const taxDistribution of TAX_DISTRIBUTIONS) {
            result.push({
              stakedThreshold,
              penaltyThreshold,
              bull,
              bear,
              taxDistribution,
            });
          }
        }
      }
    }
  }
  return result;
}

/**
 * Select `n` diverse variants from the full parameter space.
 *
 * When n ≤ total combinations (1152), samples evenly across the enumeration so
 * each axis of variation is represented. Because STAKED_THRESHOLDS is the
 * outermost loop, selectVariants(6) picks one representative per staked%
 * threshold (stride = 1152/6 = 192, one block per threshold value).
 *
 * When n > total, returns all 1152 combinations.
 */
export function selectVariants(n: number): SeedVariantParams[] {
  if (n < 1) throw new RangeError('n must be at least 1');

  const all = enumerateVariants();
  if (n >= all.length) return all;

  // Even-stride sampling across the full enumeration
  const stride = all.length / n;
  return Array.from({ length: n }, (_, i) => all[Math.floor(i * stride)]);
}
