import { DAZZA_TEXTS, type DazzaLine } from '../../data/strings/dazza';

// Pure: decides which of Dazza's texts are due. Rule-bearing texts fire on the
// clock (they are law the moment they land, design §3.1); everything else fires
// when the night gives him something to complain about, so his commentary reads
// as a reaction rather than a timer.

export interface DazzaWorld {
  vibe: number;
  aggro: number;
  queueLength: number;
  insideCount: number;
  fired: ReadonlySet<string>;
  /**
   * How many rule-bearing texts this venue's week uses (chronological prefix
   * of the rule book — venue ladder, design §6). Omitted = all of them.
   */
  ruleLimit?: number;
}

/** Conditions for the non-rule texts, keyed by DazzaLine id. */
const MOOD: Record<string, (w: DazzaWorld) => boolean> = {
  'vibe-mid': (w) => w.vibe < 35,
  'vibe-good': (w) => w.vibe > 72,
  'vibe-queue': (w) => w.queueLength >= 6,
  'vibe-empty': (w) => w.insideCount <= 3,
  kayden: (w) => w.queueLength >= 3,
};

export function dazzaDue(clockMin: number, world: DazzaWorld): DazzaLine[] {
  // The venue's rule book: the first N rule texts by fromMin. A dive doesn't
  // run the full eight; the ladder's later venues do.
  const limit = world.ruleLimit ?? Number.POSITIVE_INFINITY;
  const ruleOrder = DAZZA_TEXTS.filter((l) => l.ruleId !== undefined)
    .slice()
    .sort((a, b) => a.fromMin - b.fromMin);
  const allowedRules = new Set(ruleOrder.slice(0, limit).map((l) => l.id));

  const due: DazzaLine[] = [];
  for (const line of DAZZA_TEXTS) {
    if (world.fired.has(line.id)) continue;
    if (line.ruleId !== undefined && !allowedRules.has(line.id)) continue;
    if (clockMin < line.fromMin) continue;
    const gate = MOOD[line.id];
    if (gate && !gate(world)) continue;
    due.push(line);
  }
  // Rules first when several come due at once — the player needs the law before
  // the philosophy.
  return due.sort((a, b) => Number(!!b.ruleId) - Number(!!a.ruleId) || a.fromMin - b.fromMin);
}

/** At most one text per minute, so a quiet stretch does not become a wall of Dazza. */
export function nextDazza(clockMin: number, world: DazzaWorld): DazzaLine | undefined {
  return dazzaDue(clockMin, world)[0];
}
