/**
 * AskUser Bridge — bridges tool-generated questions to the chat UI via WebSocket.
 *
 * During background task execution, the LLM may call AskUser to get clarification.
 * This module creates pending questions that are broadcast to the UI and blocks
 * execution until the user responds (or a timeout fires).
 */

import crypto from 'crypto';

// Pending questions: questionId -> { resolve, reject, taskId, question, options, createdAt }
const _pending = new Map();

// Default timeout for unanswered questions (5 minutes)
const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;

/**
 * Create a bridge for a specific background task.
 * Returns functions that can be injected into the tool execution context.
 *
 * @param {number} taskId
 * @param {function} broadcastFn - (msg) => void, broadcasts to WebSocket clients
 * @returns {{ askUser: function, setPendingQuestion: function }}
 */
export function createBridge(taskId, broadcastFn) {
  /**
   * Ask the user a question. Returns a Promise that resolves with the answer.
   * The promise blocks the agentic loop until answered or timed out.
   *
   * @param {string} question
   * @param {string[]} options - optional list of choices
   * @param {number} timeoutMs
   * @returns {Promise<string>}
   */
  function askUser(question, options = [], timeoutMs = DEFAULT_TIMEOUT_MS) {
    const questionId = crypto.randomUUID();

    return new Promise((resolve, reject) => {
      const timer = setTimeout(() => {
        if (_pending.has(questionId)) {
          _pending.delete(questionId);
          reject(new Error(`No response within ${Math.round(timeoutMs / 1000)}s`));
        }
      }, timeoutMs);

      _pending.set(questionId, {
        resolve: (answer) => {
          clearTimeout(timer);
          _pending.delete(questionId);
          resolve(answer);
        },
        reject: (reason) => {
          clearTimeout(timer);
          _pending.delete(questionId);
          reject(new Error(reason || 'Question rejected'));
        },
        taskId,
        question,
        options,
        createdAt: Date.now()
      });

      // Broadcast to WebSocket clients
      if (broadcastFn) {
        broadcastFn({
          type: 'ask_user',
          data: { questionId, taskId, question, options }
        });
      }
    });
  }

  /**
   * Set a pending question directly (for non-promise-based usage).
   * Returns the questionId.
   */
  function setPendingQuestion(question, options = []) {
    const questionId = crypto.randomUUID();
    _pending.set(questionId, {
      resolve: () => {},
      reject: () => {},
      taskId,
      question,
      options,
      createdAt: Date.now()
    });
    if (broadcastFn) {
      broadcastFn({
        type: 'ask_user',
        data: { questionId, taskId, question, options }
      });
    }
    return questionId;
  }

  return { askUser, setPendingQuestion };
}

/**
 * Resolve a pending question with an answer.
 * @param {string} questionId
 * @param {string} answer
 * @returns {boolean} true if the question was found and resolved
 */
export function resolveAnswer(questionId, answer) {
  const entry = _pending.get(questionId);
  if (!entry) return false;
  entry.resolve(answer);
  return true;
}

/**
 * Reject a pending question.
 * @param {string} questionId
 * @param {string} reason
 * @returns {boolean} true if the question was found and rejected
 */
export function rejectAnswer(questionId, reason = 'Rejected by user') {
  const entry = _pending.get(questionId);
  if (!entry) return false;
  entry.reject(reason);
  return true;
}

/**
 * Get all currently pending questions.
 * @returns {Array<{ questionId, taskId, question, options, createdAt }>}
 */
export function getPendingQuestions() {
  const result = [];
  for (const [questionId, entry] of _pending) {
    result.push({
      questionId,
      taskId: entry.taskId,
      question: entry.question,
      options: entry.options,
      createdAt: entry.createdAt
    });
  }
  return result;
}

/**
 * Clear all pending questions (for cleanup on shutdown).
 */
export function clearPending() {
  for (const [, entry] of _pending) {
    entry.reject('Agent shutting down');
  }
  _pending.clear();
}

/**
 * Get the count of pending questions.
 * @returns {number}
 */
export function pendingCount() {
  return _pending.size;
}
