/**
 * World state flags — the backbone of GDD §6.4 puzzles and door logic.
 *
 * Deliberately a flat string set rather than structured state: it serialises
 * into the save JSONB unchanged, and in Milestone 2 the host can broadcast a
 * flag delta to peers without either side knowing what the flag means.
 */
export class FlagStore {
  private readonly flags = new Set<string>();
  private readonly listeners = new Set<(flag: string, value: boolean) => void>();

  subscribe(listener: (flag: string, value: boolean) => void): () => void {
    this.listeners.add(listener);
    return () => this.listeners.delete(listener);
  }

  set(flag: string, value = true): void {
    const changed = value ? !this.flags.has(flag) : this.flags.has(flag);
    if (!changed) return;

    if (value) this.flags.add(flag);
    else this.flags.delete(flag);

    for (const listener of this.listeners) listener(flag, value);
  }

  has(flag: string): boolean {
    return this.flags.has(flag);
  }

  /**
   * Evaluates a requirement list. A leading `!` negates, so
   * `["power-on", "!alarm-tripped"]` reads as written.
   */
  satisfies(requirements: readonly string[] | undefined): boolean {
    if (!requirements || requirements.length === 0) return true;
    return requirements.every((requirement) =>
      requirement.startsWith("!")
        ? !this.flags.has(requirement.slice(1))
        : this.flags.has(requirement),
    );
  }

  serialise(): string[] {
    return [...this.flags];
  }

  restore(flags: readonly string[]): void {
    this.flags.clear();
    for (const flag of flags) this.flags.add(flag);
  }
}
