/**
 * DualDeliveryGuard — WP-08 zero-tolerance dual delivery.
 *
 * When mode=active, legacy SessionManager / sdk.send must not fire.
 * Tracks send attempts per work key for adversarial tests.
 */

import type {
  ActiveAuthorization,
  SwitchboardDispatchMode,
} from "./SwitchboardDispatchMode.ts";
import { SwitchboardModeError } from "./SwitchboardDispatchMode.ts";

export type DeliveryPath = "legacy" | "switchboard";

export interface DualDeliveryGuardOptions {
  mode: SwitchboardDispatchMode;
  activeAuthorization?: ActiveAuthorization | null;
}

export class DualDeliveryGuard {
  #mode: SwitchboardDispatchMode;
  #auth: ActiveAuthorization | null;
  /** workKey → paths that attempted send */
  readonly #attempts = new Map<string, Set<DeliveryPath>>();
  #emergencyStopped = false;

  constructor(opts: DualDeliveryGuardOptions) {
    this.#mode = opts.mode;
    this.#auth = opts.activeAuthorization ?? null;
  }

  get mode(): SwitchboardDispatchMode {
    return this.#mode;
  }

  get emergencyStopped(): boolean {
    return this.#emergencyStopped;
  }

  setMode(
    mode: SwitchboardDispatchMode,
    auth?: ActiveAuthorization | null,
  ): void {
    if (mode === "active") {
      if (!auth?.approved || !String(auth.decisionRef ?? "").trim()) {
        throw new SwitchboardModeError(
          "cannot enter active without Gate B approve_active",
        );
      }
      this.#auth = auth;
    }
    this.#mode = mode;
    if (mode !== "off") this.#emergencyStopped = false;
  }

  /** Rollback: active|shadow → off. Clears authorization requirement for further active. */
  emergencyStop(reason = "emergency_stop"): void {
    this.#mode = "off";
    this.#auth = null;
    this.#emergencyStopped = true;
    void reason;
  }

  /**
   * Legacy sdk.send / startSession gate.
   * active → forbidden (Switchboard-only).
   * emergency stop → forbidden.
   */
  assertLegacySendAllowed(workKey?: string): void {
    if (this.#emergencyStopped) {
      throw new SwitchboardModeError(
        "legacy send blocked: emergency stop engaged (mode=off)",
      );
    }
    if (this.#mode === "active") {
      throw new SwitchboardModeError(
        "legacy send blocked: mode=active — only HostSwitchboard may call Host",
      );
    }
    if (workKey) this.recordAttempt(workKey, "legacy");
  }

  /** Switchboard Host startRun gate. */
  assertSwitchboardSendAllowed(workKey?: string): void {
    if (this.#emergencyStopped || this.#mode === "off") {
      throw new SwitchboardModeError(
        "switchboard send blocked: mode=off (or emergency stop)",
      );
    }
    if (this.#mode === "active") {
      if (!this.#auth?.approved) {
        throw new SwitchboardModeError(
          "switchboard active send blocked: missing approve_active",
        );
      }
    }
    if (workKey) this.recordAttempt(workKey, "switchboard");
  }

  recordAttempt(workKey: string, path: DeliveryPath): void {
    const key = String(workKey ?? "").trim();
    if (!key) return;
    let set = this.#attempts.get(key);
    if (!set) {
      set = new Set();
      this.#attempts.set(key, set);
    }
    set.add(path);
  }

  /** True when both legacy and switchboard attempted the same work key. */
  isDualDelivery(workKey: string): boolean {
    const set = this.#attempts.get(String(workKey ?? "").trim());
    return Boolean(set?.has("legacy") && set?.has("switchboard"));
  }

  attemptsFor(workKey: string): DeliveryPath[] {
    return [...(this.#attempts.get(String(workKey ?? "").trim()) ?? [])];
  }

  clearAttempts(): void {
    this.#attempts.clear();
  }
}
