// Tools whose argument JSON carries a large content field worth streaming live.
// Maps tool name → the argument key to extract and render.
export const CONTENT_FIELD: Record<string, string> = {
  Write: 'content',
  Edit: 'new_string',
};

const ESCAPES: Record<string, string> = {
  n: '\n', t: '\t', r: '\r', b: '\b', f: '\f', '"': '"', '\\': '\\', '/': '/',
};

/**
 * Decode a JSON string literal beginning at raw[start] === '"'.
 * Returns the unescaped value and the index just past the closing quote.
 * For a still-streaming (unterminated, or escape split across a chunk
 * boundary) string, returns end === -1 and value === the decoded prefix.
 */
function parseString(raw: string, start: number): { value: string; end: number } {
  const n = raw.length;
  let j = start + 1; // skip opening quote
  let out = '';
  while (j < n) {
    const c = raw[j]!;
    if (c === '\\') {
      if (j + 1 >= n) return { value: out, end: -1 }; // dangling backslash — wait for more
      const esc = raw[j + 1]!;
      if (esc === 'u') {
        if (j + 6 > n) return { value: out, end: -1 }; // incomplete \uXXXX — wait for more
        out += String.fromCharCode(parseInt(raw.slice(j + 2, j + 6), 16));
        j += 6;
        continue;
      }
      out += ESCAPES[esc] ?? esc;
      j += 2;
      continue;
    }
    if (c === '"') return { value: out, end: j + 1 };
    out += c;
    j++;
  }
  return { value: out, end: -1 }; // unterminated — still streaming
}

/**
 * Given a tool name and the accumulated (possibly partial) JSON argument
 * string, return the current decoded value of the tool's content field.
 * Pure and idempotent — re-scans `raw` from the start on every call, so it
 * is naturally resilient to chunk boundaries. Returns '' if the tool has no
 * mapped field or the field's value has not started yet.
 */
export function extractStreamingField(toolName: string, raw: string): string {
  const field = CONTENT_FIELD[toolName];
  if (!field) return '';
  const n = raw.length;

  let i = 0;
  while (i < n && raw[i] !== '{') i++; // skip to object start
  if (i >= n) return '';
  i++; // enter object (depth 1)

  while (i < n) {
    const c = raw[i]!;
    if (c === '}') break;
    if (c === ',' || c === ' ' || c === '\n' || c === '\t' || c === '\r') { i++; continue; }
    if (c !== '"') { i++; continue; } // unexpected; skip defensively

    // Read the key.
    const keyRes = parseString(raw, i);
    if (keyRes.end === -1) return ''; // key still streaming
    const key = keyRes.value;
    i = keyRes.end;

    // Skip to ':' then whitespace.
    while (i < n && raw[i] !== ':') i++;
    if (i >= n) return '';
    i++; // skip ':'
    while (i < n && (raw[i] === ' ' || raw[i] === '\n' || raw[i] === '\t' || raw[i] === '\r')) i++;
    if (i >= n) return '';

    if (key === field) {
      if (raw[i] !== '"') return ''; // value not a string (or not started)
      return parseString(raw, i).value; // decoded prefix, terminated or not
    }

    // Not our key — skip the value.
    if (raw[i] === '"') {
      const valRes = parseString(raw, i);
      if (valRes.end === -1) return ''; // non-target value still streaming; cannot reach target yet
      i = valRes.end;
    } else {
      // number / boolean / null / object / array
      let depth = 0;
      while (i < n) {
        const ch = raw[i]!;
        if (ch === '"') { const s = parseString(raw, i); if (s.end === -1) return ''; i = s.end; continue; }
        if (ch === '{' || ch === '[') { depth++; i++; continue; }
        if (ch === '}' || ch === ']') { if (depth === 0) break; depth--; i++; continue; }
        if (ch === ',' && depth === 0) break;
        i++;
      }
    }
  }
  return '';
}
