// SPEC: PJ2026-0106050514 Workbench实时运行面 draft-2026-06-30-p0-1297-spec-first.
// Responsibility: Keyed single-flight primitive for Workbench REST refresh and health probes.

export interface KeyedSingleflightContext {
  key: string;
  signal: AbortSignal;
  generation: number;
  reason?: string | null;
}

export interface KeyedSingleflightRunOptions {
  replace?: boolean;
  reason?: string | null;
}

interface InflightEntry<T> {
  generation: number;
  controller: AbortController;
  promise: Promise<T>;
}

export class KeyedSingleflight<T> {
  private readonly inflight = new Map<string, InflightEntry<T>>();
  private readonly generations = new Map<string, number>();

  get size(): number {
    return this.inflight.size;
  }

  run(key: string, task: (context: KeyedSingleflightContext) => Promise<T> | T, options: KeyedSingleflightRunOptions = {}): Promise<T> {
    const normalized = normalizeKey(key);
    const existing = this.inflight.get(normalized);
    if (existing && !options.replace) return existing.promise;
    if (existing) existing.controller.abort(options.reason ?? "singleflight-replaced");

    const generation = (this.generations.get(normalized) ?? 0) + 1;
    this.generations.set(normalized, generation);
    const controller = new AbortController();
    const promise = Promise.resolve()
      .then(() => task({ key: normalized, signal: controller.signal, generation, reason: options.reason ?? null }))
      .finally(() => {
        const current = this.inflight.get(normalized);
        if (current?.generation === generation) this.inflight.delete(normalized);
      });
    this.inflight.set(normalized, { generation, controller, promise });
    return promise;
  }

  cancel(key: string, reason?: string): boolean {
    const normalized = normalizeKey(key);
    const existing = this.inflight.get(normalized);
    if (!existing) return false;
    existing.controller.abort(reason ?? "singleflight-canceled");
    this.inflight.delete(normalized);
    return true;
  }

  invalidate(key: string): void {
    const normalized = normalizeKey(key);
    this.generations.set(normalized, (this.generations.get(normalized) ?? 0) + 1);
    this.cancel(normalized, "singleflight-invalidated");
  }

  isCurrent(key: string, generation: number): boolean {
    return (this.generations.get(normalizeKey(key)) ?? 0) === generation;
  }

  clear(reason = "singleflight-cleared"): void {
    for (const entry of this.inflight.values()) entry.controller.abort(reason);
    this.inflight.clear();
  }
}

export function createKeyedSingleflight<T>(): KeyedSingleflight<T> {
  return new KeyedSingleflight<T>();
}

function normalizeKey(key: string): string {
  const text = key.trim();
  if (!text) throw new Error("singleflight key is required");
  return text;
}

