// Builds cambium-grammar command lines for the GUI's file-control verbs
// (checkout/add/delete/revert/submit/pull, ...) and runs them through the
// shared structured executor path (`run_command_structured`) the console
// panel's grammar also goes through — Tasks 3-4's verb rewiring build every
// verb on top of `buildCommandLine` + `dispatchFileCommand` instead of each
// reinventing quoting and refresh wiring.
import { runCommandStructured } from '../platform';
import { applyConsoleChanged, buildConsoleCtx } from './consoleDispatch';

/**
 * Quotes `s` for `cambium_core::commands::parse_line`, which splits a
 * console line with `shlex::split` (POSIX-ish: inside double quotes,
 * backslash escapes `"` and `\`; a bare backslash outside quotes is also an
 * escape character there). A plain path with no whitespace/quote/backslash
 * round-trips unquoted; anything else is wrapped in double quotes with `\`
 * escaped first, then `"` — so a literal backslash never swallows a
 * following escaped quote — which is exactly what shlex's own double-quote
 * unescaping expects back.
 */
export function quoteArg(s) {
  if (s !== '' && !/[\s"'\\]/.test(s)) return s;
  const escaped = s.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
  return `"${escaped}"`;
}

/**
 * Builds one cambium console line for a file-control verb: `paths` become
 * depot paths (prefixed with `//` unless already one), each quoted; `-f` is
 * appended when `force`; `-m <message>` (quoted) is appended when `message`
 * is given. Matches the command reference's argument order — paths, then
 * flags — for every verb that takes both.
 */
export function buildCommandLine(verb, paths = [], { force, message } = {}) {
  const parts = [verb];
  for (const path of paths) {
    const depotPath = path.startsWith('//') ? path : `//${path}`;
    parts.push(quoteArg(depotPath));
  }
  if (force) parts.push('-f');
  if (message !== undefined) parts.push('-m', quoteArg(message));
  return parts.join(' ');
}

/**
 * Runs `line` through the shared structured executor (same grammar +
 * ExecContext the console panel and the `cambium` CLI use) and, on success,
 * fans the result's `changed` hints out to the refreshers — the same
 * `applyConsoleChanged` the console uses, plus a full workspace reload when
 * `changed` includes `'files'` (a file-control verb touches local disk state
 * the console's read-only commands never do). `runner` is injectable for
 * tests (defaults to `runCommandStructured`); `onProgress` rides straight
 * through so a submit/pull issued this way can drive the toolbar progress
 * strip.
 */
export async function dispatchFileCommand(
  line,
  conn,
  refreshers,
  { onProgress, runner = runCommandStructured } = {}
) {
  const ctx = buildConsoleCtx(conn);
  const result = await runner(line, ctx, onProgress);
  if (result.ok) {
    applyConsoleChanged(result.changed, refreshers);
    if (result.changed?.includes('files')) {
      await refreshers.reloadWorkspace?.();
    }
  }
  return result;
}
