/**
 * ApprovalGate — Promise-based L2 tool approval with configurable timeout.
 *
 * Used inside the Agent SDK's `canUseTool` callback to block execution
 * on high-risk (L2) commands until the user approves or the timeout expires.
 *
 * Flow:
 * 1. SDK calls canUseTool → classifyToolRisk returns HIGH_RISK (L2)
 * 2. Engine emits 'approval_required' event via WebSocket to client
 * 3. waitForApproval() blocks on a Promise
 * 4. Either:
 *    a. User calls POST /tasks/:id/approve → resolveApproval(true/false)
 *    b. Timeout expires → auto-approve (resolve(true))
 * 5. canUseTool returns allow/deny based on result
 *
 * Timeout of 0 means wait indefinitely (no auto-approve).
 */
import * as crypto from 'crypto';

interface PendingApproval {
  resolve: (approved: boolean) => void;
  timer: NodeJS.Timeout;
  toolName: string;
  toolInput: Record<string, unknown>;
}

export class ApprovalGate {
  private pendingApprovals = new Map<string, PendingApproval>();

  constructor(private readonly timeoutSeconds: number) {}

  async waitForApproval(
    toolName: string,
    toolInput: Record<string, unknown>,
  ): Promise<boolean> {
    const id = crypto.randomUUID();

    return new Promise<boolean>((resolve) => {
      const timer = setTimeout(() => {
        this.pendingApprovals.delete(id);
        resolve(true); // Auto-approve on timeout
      }, this.timeoutSeconds * 1000);

      this.pendingApprovals.set(id, { resolve, timer, toolName, toolInput });
    });
  }

  resolveApproval(approved: boolean): void {
    // Resolve the most recent pending approval (LIFO)
    const entries = [...this.pendingApprovals.entries()];
    const entry = entries[entries.length - 1];
    if (!entry) return;

    const [id, { resolve, timer }] = entry;
    clearTimeout(timer);
    this.pendingApprovals.delete(id);
    resolve(approved);
  }

  hasPending(): boolean {
    return this.pendingApprovals.size > 0;
  }

  dispose(): void {
    for (const [id, { timer, resolve }] of this.pendingApprovals) {
      clearTimeout(timer);
      resolve(false);
    }
    this.pendingApprovals.clear();
  }
}
