import { checkCommand, validateCustomPatterns } from './deny-list.js';

/**
 * Result of running each non-empty line of a console input chunk through
 * the deny/allow regex layers. On failure the caller surfaces line index +
 * matched pattern to the LLM (for AI input) or to the operator UI (for
 * human input via the WS bridge), so they can correct the specific line.
 */
export type ConsoleDenyResult =
  | { ok: true }
  | {
      ok: false;
      lineIndex: number;
      line: string;
      reason: 'builtin_deny' | 'custom_deny';
      matched: string | null;
    };

function compileList(sources: string[] | null): RegExp[] {
  if (!sources || sources.length === 0) return [];
  const r = validateCustomPatterns(sources);
  return r.ok && r.compiled ? r.compiled : [];
}

/**
 * Line-wise wrapper around deny-list.checkCommand. Splits `input` on
 * \r?\n, trims each line, skips empties, and rejects on the first failing
 * line. Custom deny / allow patterns are compiled once per call.
 *
 * Allowlist semantics ('not_in_allowlist') are surfaced as 'custom_deny'
 * since to the operator/agent that's the same actionable state: the line
 * was refused by policy, not by a built-in.
 */
export function checkConsoleInput(
  input: string,
  customDenyPatterns: string[] | null,
  customAllowPatterns: string[] | null,
): ConsoleDenyResult {
  const lines = input.split(/\r?\n/);
  const customDeny = compileList(customDenyPatterns);
  const customAllow = compileList(customAllowPatterns);
  for (let i = 0; i < lines.length; i++) {
    const line = lines[i]!.trim();
    if (line.length === 0) continue;
    const r = checkCommand({
      command: line,
      customDenyPatterns: customDeny,
      customAllowPatterns: customAllow,
    });
    if (!r.allowed) {
      return {
        ok: false,
        lineIndex: i,
        line,
        reason: r.reason === 'builtin_deny' ? 'builtin_deny' : 'custom_deny',
        matched: r.matched ?? null,
      };
    }
  }
  return { ok: true };
}
