/**
 * NXL codec — compact wire format for MCP responses.
 *
 * Achieves ~70% token reduction vs JSON by:
 *   - Key shortcodes: "status" → "s", "progress" → "p", etc.
 *   - Omitting null/undefined fields
 *   - Compact boolean: true → 1, false → 0
 *   - Short number format (no trailing zeros)
 *   - Flat k:v space-separated for simple objects
 *   - NXLB block format for arrays/nested objects
 *
 * Wire contract: responses prefixed with `NXL1 ` marker.
 * Clients that don't understand NXL request format=json (default).
 */
// ─── Field shortcode table ─────────────────────────────────────────────────────
const ENCODE_KEYS = {
    runId: 'id', jobId: 'jid', workerId: 'wid', claimId: 'cid',
    status: 's', stage: 'st', progress: 'p', goal: 'g',
    tool: 't', error: 'e', result: 'r', client: 'c',
    title: 'ti', tier: 'tr', priority: 'pr',
    budgetCapUsd: 'bu', spentUsd: 'sp', score: 'sc',
    tokensUsed: 'tok', name: 'n', role: 'ro',
    startedAt: 'sa', updatedAt: 'ua', completedAt: 'ca',
    queuedAt: 'qa', expiresAt: 'ex', lane: 'l',
    totalJobs: 'tn', promoted: 'prom', blocked: 'blk',
    queued: 'q', etaMs: 'eta', elapsed: 'el',
    dependsOn: 'dep', parentId: 'pid', resultSummary: 'rs',
    inFlight: 'inf', retryAfterMs: 'rat',
    payload: 'pay', metadata: 'meta',
};
const DECODE_KEYS = Object.fromEntries(Object.entries(ENCODE_KEYS).map(([k, v]) => [v, k]));
// ─── Encoder ──────────────────────────────────────────────────────────────────
function encodeValue(v, depth) {
    if (v === null || v === undefined)
        return '-';
    // Use 't'/'f' for booleans to avoid collision with numeric 0/1
    if (typeof v === 'boolean')
        return v ? 't' : 'f';
    if (typeof v === 'number') {
        // String(v) preserves full float64 precision; avoids toPrecision truncation
        return String(v);
    }
    if (typeof v === 'string') {
        // quote only if contains spaces or special chars
        if (/[\s:[\]{},]/.test(v))
            return `"${v.replace(/"/g, '\\"')}"`;
        return v;
    }
    if (Array.isArray(v)) {
        if (v.length === 0)
            return '[]';
        const items = v.map(item => encodeValue(item, depth + 1));
        // short arrays inline
        if (items.every(i => !i.includes('\n')) && items.join(',').length < 60) {
            return `[${items.join(',')}]`;
        }
        const indent = '  '.repeat(depth + 1);
        return `[\n${items.map(i => `${indent}${i}`).join('\n')}\n${'  '.repeat(depth)}]`;
    }
    if (typeof v === 'object') {
        return encodeObject(v, depth);
    }
    return String(v);
}
function encodeObject(obj, depth) {
    const pairs = [];
    for (const [k, v] of Object.entries(obj)) {
        if (v === null || v === undefined)
            continue;
        const shortKey = ENCODE_KEYS[k] ?? k;
        pairs.push(`${shortKey}:${encodeValue(v, depth + 1)}`);
    }
    if (pairs.length === 0)
        return '{}';
    // flat inline for simple shallow objects
    if (depth === 0 && pairs.every(p => !p.includes('\n')) && pairs.join(' ').length < 120) {
        return pairs.join(' ');
    }
    const indent = '  '.repeat(depth + 1);
    return `{\n${pairs.map(p => `${indent}${p}`).join('\n')}\n${'  '.repeat(depth)}}`;
}
/** Encode an object/array to NXL wire format string. */
export function encodeNxl(data) {
    const body = encodeValue(data, 0);
    return `NXL1 ${body}`;
}
// ─── Decoder ──────────────────────────────────────────────────────────────────
/** Decode an NXL wire format string back to a plain object. */
export function decodeNxl(nxl) {
    const body = nxl.startsWith('NXL1 ') ? nxl.slice(5) : nxl;
    const [result] = parseValue(body.trim(), 0);
    return result;
}
function restoreKey(k) {
    return DECODE_KEYS[k] ?? k;
}
function parseValue(s, pos) {
    const c = s[pos];
    if (c === '[')
        return parseArray(s, pos);
    if (c === '{')
        return parseObjectBlock(s, pos);
    if (c === '"')
        return parseQuoted(s, pos);
    if (s.startsWith('-', pos) && (pos + 1 >= s.length || /[\s,\]}]/.test(s[pos + 1]))) {
        return [null, pos + 1];
    }
    // read token until whitespace / : / , / ] / }
    let end = pos;
    while (end < s.length && !/[\s:,[\]{}\n]/.test(s[end]))
        end++;
    const token = s.slice(pos, end);
    if (token === 't')
        return [true, end];
    if (token === 'f')
        return [false, end];
    const num = Number(token);
    if (!Number.isNaN(num) && token !== '')
        return [num, end];
    return [token, end];
}
function parseQuoted(s, pos) {
    let i = pos + 1;
    let str = '';
    while (i < s.length) {
        if (s[i] === '\\' && s[i + 1] === '"') {
            str += '"';
            i += 2;
            continue;
        }
        if (s[i] === '"') {
            i++;
            break;
        }
        str += s[i++];
    }
    return [str, i];
}
function parseArray(s, pos) {
    let i = pos + 1;
    const arr = [];
    while (i < s.length) {
        while (i < s.length && /[\s,\n]/.test(s[i]))
            i++;
        if (s[i] === ']') {
            i++;
            break;
        }
        const [val, next] = parseValue(s, i);
        arr.push(val);
        i = next;
    }
    return [arr, i];
}
function parseObjectBlock(s, pos) {
    let i = pos + 1;
    const obj = {};
    while (i < s.length) {
        while (i < s.length && /[\s\n,]/.test(s[i]))
            i++;
        if (s[i] === '}') {
            i++;
            break;
        }
        // read key
        let keyEnd = i;
        while (keyEnd < s.length && s[keyEnd] !== ':')
            keyEnd++;
        const shortKey = s.slice(i, keyEnd).trim();
        i = keyEnd + 1;
        const [val, next] = parseValue(s, i);
        obj[restoreKey(shortKey)] = val;
        i = next;
    }
    return [obj, i];
}
/** Parse flat k:v space-separated line (top-level NXL1 objects) */
export function decodeFlatLine(line) {
    const s = line.startsWith('NXL1 ') ? line.slice(5) : line;
    // If it's a block object, parse normally
    if (s.trimStart().startsWith('{')) {
        const [result] = parseObjectBlock(s.trim(), 0);
        return result;
    }
    // Flat k:v pairs
    const obj = {};
    const re = /(\w+):("(?:[^"\\]|\\.)*"|[^\s]+)/g;
    let m;
    while ((m = re.exec(s)) !== null) {
        const [, k, v] = m;
        const [parsed] = parseValue(v, 0);
        obj[restoreKey(k)] = parsed;
    }
    return obj;
}
/** Whether a string is NXL-encoded. */
export function isNxl(s) {
    return s.startsWith('NXL1 ');
}
