'use strict';
// Cursor Agent approval-prompt provider.
//
// The current `cursor-agent` CLI renders a "Run this command?" widget for shell
// commands with single-key HOTKEYS (not a numbered 1./2. list):
//
//     Run this command?
//     Not in allowlist: cd /repo, git status, git fetch origin main
//     → Run (once) (y)
//       Add Shell(cd), Shell(git status), Shell(git fetch) to allowlist? (tab)
//       Auto-run everything (shift+tab)
//       Skip (esc or n)
//
// The selection cursor is "→" and the confirm hotkey is "y" (runs immediately,
// no Enter). We treat the underlying action as a shell command (toolName
// "Bash command") so it gets a per-command signature and is matched by the
// user's Bash(...) perm_rules / blocklist across providers — collapsing it to a
// generic "Cursor Agent" tool name would group every command into one signature.
//
// We also keep backward-compat detection for an older numbered
// "Do you want to …? / 1. Yes" widget shape.

// --- Current "Run this command?" hotkey format ---------------------------------
// Confirm option. Optional leading selection cursor (→/->/▸/❯/›/▶/>) then
// "Run (once) (y)". The "(y)" hotkey is the stable, distinctive marker.
const RUN_ONCE_RE = /(?:^|\s)(?:[▸❯›▶→>-]+\s*)?Run\s*\(once\)\s*\(y\)/i;
// Exit/secondary options — any one of these alongside Run-once confirms the shape.
const SKIP_RE = /\(esc\s+or\s+n\)/i;
const AUTORUN_RE = /Auto-run everything[^\n]*\(shift\+tab\)/i;
const ALLOWLIST_OPT_RE = /to allowlist\?\s*\(tab\)/i;
const NOT_IN_ALLOWLIST_RE = /^\s*Not in allowlist:\s*(.+)$/i;
const RUN_HEADER_RE = /^\s*Run this command\?\s*$/i;
// UI/chrome lines to skip when hunting for the echoed command.
const RUN_UI_LINE_RE = /^(?:run this command\??|not in allowlist:|add\b.*allowlist\?|auto-run everything|skip\b)/i;

// --- Legacy numbered widget (best-effort; may not exist in current CLI) --------
const LEGACY_PROMPT_RE = /Do you want to .+\?|Agent wants to|Allow Cursor to/i;
const LEGACY_YES_RE = /^\s*1\.\s*Yes\b/i;
const LEGACY_ALLOW_ALL_RE = /2\.\s*(Yes,?\s*(allow all|always|and always)|Always allow)/i;

function _detectRunFormat(tail) {
  const hasRunOnce = tail.some(l => RUN_ONCE_RE.test(l));
  if (!hasRunOnce) return false;
  // Require a second, format-stable signal so a stray "Run (once) (y)" in prose
  // doesn't false-positive.
  return tail.some(l => SKIP_RE.test(l) || AUTORUN_RE.test(l) || ALLOWLIST_OPT_RE.test(l));
}

function _detectLegacyFormat(tail) {
  const hasPrompt = tail.some(l => LEGACY_PROMPT_RE.test(l));
  const hasYes = tail.some(l => LEGACY_YES_RE.test(l));
  return hasPrompt && hasYes;
}

function _parseRunFormat(lines) {
  let runIdx = -1;
  for (let i = lines.length - 1; i >= 0; i--) {
    if (RUN_ONCE_RE.test(lines[i])) { runIdx = i; break; }
  }
  if (runIdx < 0) return null;

  // Real command: prefer the purpose-built "Not in allowlist:" line (single line,
  // lists exactly the commands needing approval). Fall back to the echoed command
  // line above the "Run this command?" header.
  let command = '';
  for (let i = runIdx; i >= 0 && i >= runIdx - 8; i--) {
    const m = lines[i].match(NOT_IN_ALLOWLIST_RE);
    if (m) { command = m[1].trim(); break; }
  }
  if (!command) {
    let anchor = runIdx;
    for (let i = runIdx; i >= 0 && i >= runIdx - 6; i--) {
      if (RUN_HEADER_RE.test(lines[i])) { anchor = i; break; }
    }
    for (let i = anchor - 1; i >= 0 && i >= anchor - 10; i--) {
      const l = lines[i].replace(/^[$%#>\s]+/, '').trim();
      if (!l || RUN_UI_LINE_RE.test(l)) continue;
      command = l.slice(0, 2000);
      break;
    }
  }

  const hasAllowAll = lines.some(l => AUTORUN_RE.test(l) || ALLOWLIST_OPT_RE.test(l));
  const ctxStart = Math.max(0, runIdx - 12);
  const fullContext = lines.slice(ctxStart, Math.min(lines.length, runIdx + 4)).join('\n').slice(0, 2000);
  return {
    toolName: 'Bash command',
    command,
    warning: '',
    fullContext,
    hasAllowAll,
    format: 'run-command',
    providerId: 'cursor',
  };
}

function _parseLegacyFormat(lines) {
  let promptIdx = -1;
  for (let i = lines.length - 1; i >= 0; i--) {
    if (LEGACY_PROMPT_RE.test(lines[i])) { promptIdx = i; break; }
  }
  if (promptIdx < 0) return null;

  let yesIdx = -1;
  for (let i = promptIdx + 1; i < Math.min(promptIdx + 6, lines.length); i++) {
    if (LEGACY_YES_RE.test(lines[i])) { yesIdx = i; break; }
  }
  if (yesIdx < 0) return null;

  const hasAllowAll = lines.slice(promptIdx + 1, promptIdx + 8).some(l => LEGACY_ALLOW_ALL_RE.test(l));
  const ctxStart = Math.max(0, promptIdx - 10);
  const command = lines.slice(ctxStart, promptIdx).join('\n').slice(0, 2000);
  const fullContext = lines.slice(ctxStart, yesIdx + 3).join('\n').slice(0, 2000);
  return {
    toolName: 'Cursor Agent',
    command,
    warning: '',
    fullContext,
    hasAllowAll,
    format: 'numbered',
    providerId: 'cursor',
  };
}

module.exports = {
  id: 'cursor',
  name: 'Cursor Agent',

  detect(text) {
    const lines = String(text || '').split('\n').filter(l => l.trim());
    if (lines.length < 2) return false;
    const tail = lines.slice(-14);
    return _detectRunFormat(tail) || _detectLegacyFormat(tail);
  },

  parse(text) {
    const lines = String(text || '').split('\n').map(l => l.trim()).filter(Boolean);
    if (!lines.length) return null;
    const tail = lines.slice(-14);
    if (_detectRunFormat(tail)) return _parseRunFormat(lines);
    return _parseLegacyFormat(lines);
  },

  approveKeystroke(ctx) {
    // Current format: single-key "y" runs once (no Enter — see requiresEnter).
    if (ctx && ctx.format === 'run-command') return 'y';
    // Legacy numbered: "2" only when it's an allow-all option, else "1".
    return ctx && ctx.hasAllowAll ? '2' : '1';
  },

  approveAllKeystroke(ctx) {
    // Never auto-enable Cursor's "Auto-run everything" / allowlist-widen from the
    // shadow approver — those broaden far beyond the single command. Allow-all is
    // expressed via CTM perm_rules instead. Legacy numbered keeps its "2" for
    // parity with prior behavior.
    if (ctx && ctx.format === 'run-command') return null;
    return ctx && ctx.hasAllowAll ? '2' : null;
  },

  // "y" / "1" are direct hotkeys that fire immediately — no Enter to confirm.
  requiresEnter: false,
};
