/**
 * Cassette Normalizer
 *
 * Strips dynamic content from prompts and CLI args before hashing into a cassette key.
 * Dynamic content (UUIDs, temp paths, timestamps, session numbers) varies between
 * test runs but doesn't affect how the agent responds — so we replace them with
 * stable placeholders to get a stable cache key.
 */

const UUID_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi;
const NANOID_RE = /(?<![A-Za-z0-9])[A-Za-z0-9_-]{21}(?![A-Za-z0-9_-])/g;
const ISO_TIMESTAMP_RE = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})?/g;
const UNIX_EPOCH_MS_RE = /\b1[0-9]{12}\b/g;
const SESSION_NUM_RE = /\bsession[_\s-]?\d+\b/gi;
// Agent worktree paths: agent-workdirs/<random-agent-name> (with or without trailing slash)
// The agent name (e.g. "available-sheep") changes every run but is not a UUID or nanoid.
// Stop at the first slash so the project name after it is preserved.
const AGENT_WORKDIR_RE = /agent-workdirs\/[^\s/\\]+/g;

/**
 * Normalize a prompt for stable cassette key generation.
 *
 * Replacements applied in order (most-specific first to avoid partial matches):
 * 1. Absolute workspace root path → __WORKSPACE__
 * 2. UUIDs → __UUID__
 * 2.5. Nanoid IDs (21-char alphanumeric) → __ID__
 * 3. ISO 8601 timestamps → __TIMESTAMP__
 * 4. Unix epoch milliseconds → __EPOCH__
 * 5. Session numbers → session__N__
 * 6. Agent worktree path segment → agent-workdirs/__AGENT__/
 */
export function normalizePrompt(prompt: string, workspaceRoot: string): string {
  let normalized = prompt;

  if (workspaceRoot) {
    normalized = normalized.replaceAll(workspaceRoot, '__WORKSPACE__');
  }

  normalized = normalized.replace(UUID_RE, '__UUID__');
  normalized = normalized.replace(NANOID_RE, '__ID__');
  normalized = normalized.replace(ISO_TIMESTAMP_RE, '__TIMESTAMP__');
  normalized = normalized.replace(UNIX_EPOCH_MS_RE, '__EPOCH__');
  normalized = normalized.replace(SESSION_NUM_RE, 'session__N__');
  normalized = normalized.replace(AGENT_WORKDIR_RE, 'agent-workdirs/__AGENT__');

  return normalized;
}

/**
 * Strip the prompt value from CLI args to produce stable modelArgs for the cassette key.
 *
 * Handles all provider prompt flag styles:
 * - Native: `-p <prompt>` (Claude)
 * - Flag: `--prompt <prompt>`, `-p <prompt>` (Gemini, Cursor, Auggie, Amp, Opencode)
 * - Also removes the bare prompt value if it appears as a positional arg.
 */
export function stripPromptFromArgs(args: string[], prompt: string): string[] {
  if (!prompt) return [...args];

  const result: string[] = [];
  let i = 0;
  while (i < args.length) {
    const arg = args[i];
    const PROMPT_FLAGS = ['-p', '--prompt', '--message'];

    if (PROMPT_FLAGS.includes(arg) && args[i + 1] === prompt) {
      i += 2; // skip flag + value
    } else if (arg === prompt) {
      i += 1; // skip bare positional prompt
    } else {
      result.push(arg);
      i++;
    }
  }
  return result;
}
