/**
 * public/fanout.ts (W5 fan-out board) — PURE launch-command + branch-slug helpers.
 *
 * No DOM, no fetch, no side effects — every export is unit-testable in isolation.
 * The load-bearing invariant is the prompt shell-quoting (shellSingleQuote): the
 * prompt is typed as raw bytes into a lane's shell BEFORE `claude` parses it, so
 * it must be wrapped as a single literal argv element — no `$(...)`, backtick,
 * `;`, `&&`, or redirection may execute. NEVER String-concat the prompt into the
 * command unquoted (see the plan's Security section).
 */

import type { PermissionMode } from '../src/types.js'

/** Max prompt length typed into a lane (bounded DoS + keeps the command sane). */
export const FANOUT_PROMPT_MAX = 4000

/** Default/max fan-out lanes (mirrors the server MAX_FANOUT_LANES default and the
 *  grid-6 board capacity). The FE stepper max and the launch clamp both use it. */
export const FANOUT_MAX_LANES = 6

/**
 * POSIX single-quote a string so the shell passes it verbatim as ONE argv element.
 * Wrap in single quotes and replace every embedded `'` with the classic
 * `'\''` sequence (close-quote, escaped-quote, re-open-quote). Pure.
 */
export function shellSingleQuote(s: string): string {
  return `'${s.replace(/'/g, `'\\''`)}'`
}

/**
 * Normalize a prompt for typing into a PTY: collapse every line break (`\r`, `\n`,
 * `\r\n`) to a single space — a bare newline keystroke would submit a partial line
 * before `claude` starts — then trim and cap at FANOUT_PROMPT_MAX. Pure.
 */
export function sanitizePrompt(s: string): string {
  return s.replace(/[\r\n]+/g, ' ').trim().slice(0, FANOUT_PROMPT_MAX)
}

/**
 * Build the `claude` launch command for one lane. Single-quotes the sanitized
 * prompt (injection-safe), prefixes `--permission-mode <m>` for non-default modes,
 * and ends with `\r` so the shell auto-executes it. 'auto' is downgraded to
 * 'default' when the server forbids it (SEC-M5 parity with resolveMode). Pure.
 */
export function buildFanoutCmd(
  prompt: string,
  mode: PermissionMode,
  allowAutoMode: boolean,
): string {
  const effective: PermissionMode = mode === 'auto' && !allowAutoMode ? 'default' : mode
  const flag = effective === 'default' ? '' : `--permission-mode ${effective} `
  return `claude ${flag}${shellSingleQuote(sanitizePrompt(prompt))}\r`
}

/** Lane branch name: `${base}-lane-${i}` (i is 1-based). Pure. */
export function laneBranch(base: string, i: number): string {
  return `${base}-lane-${i}`
}

/**
 * Derive a safe git-branch-base slug from a free-text prompt: lowercase, non-
 * alphanumeric runs → single `-`, strip leading/trailing dashes, cap at 40 chars.
 * Falls back to 'fanout' when nothing survives. The result is a valid branch base
 * (passes validateBranchNameClient). Pure.
 */
export function slugify(prompt: string): string {
  const slug = prompt
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '')
    .slice(0, 40)
    .replace(/-+$/g, '')
  return slug || 'fanout'
}
