const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
function looksLikeKnownProviderResponseShape(value) {
    if (!isRecord(value))
        return false;
    if (Array.isArray(value.choices))
        return true; // openai-chat
    if (Array.isArray(value.output) || value.object === 'response')
        return true; // openai-responses
    if (typeof value.type === 'string' && String(value.type).toLowerCase() === 'message' && Array.isArray(value.content)) {
        return true; // anthropic-messages
    }
    if (Array.isArray(value.candidates))
        return true; // gemini-chat
    return false;
}
function stripJsonTextPrefix(text) {
    let out = String(text || '').trimStart();
    // anti-XSSI prefix: ")]}',\n{...}"
    out = out.replace(/^\)\]\}',?\s*/u, '');
    // single-line SSE-like wrapper sometimes returned as plain text
    out = out.replace(/^data:\s*/iu, '');
    return out;
}
function tryParseJsonRecord(text) {
    try {
        const trimmed = stripJsonTextPrefix(text).trim();
        if (!trimmed)
            return null;
        if (trimmed.length > 10 * 1024 * 1024)
            return null;
        const first = trimmed.charAt(0);
        const last = trimmed.charAt(trimmed.length - 1);
        if ((first !== '{' && first !== '[') || (last !== '}' && last !== ']')) {
            return null;
        }
        const parsed = JSON.parse(trimmed);
        return isRecord(parsed) ? parsed : null;
    }
    catch {
        return null;
    }
}
function tryParseJsonRecordFromMaybeSseText(text) {
    if (typeof text !== 'string') {
        return null;
    }
    const raw = stripJsonTextPrefix(text);
    const direct = tryParseJsonRecord(raw);
    if (direct) {
        return direct;
    }
    // Multi-line SSE text occasionally shows up as a plain string inside iFlow envelopes.
    // Best-effort: parse the last valid `data:` JSON object chunk.
    if (raw.length > 10 * 1024 * 1024) {
        return null;
    }
    const lines = raw.split(/\r?\n/u);
    for (let i = lines.length - 1; i >= 0; i -= 1) {
        const line = lines[i]?.trim();
        if (!line)
            continue;
        const lower = line.toLowerCase();
        if (lower === 'data: [done]' || lower === '[done]')
            continue;
        const candidate = lower.startsWith('data:') ? line.slice(5).trim() : line;
        const parsed = tryParseJsonRecord(candidate);
        if (parsed) {
            return parsed;
        }
    }
    return null;
}
function tryUnwrapKnownShapeFromBody(value, depth) {
    if (depth < 0)
        return null;
    if (looksLikeKnownProviderResponseShape(value)) {
        return value;
    }
    if (isRecord(value)) {
        // nested body/data wrappers
        for (const key of ['data', 'body', 'response', 'payload', 'result']) {
            const nested = value[key];
            const unwrapped = tryUnwrapKnownShapeFromBody(nested, depth - 1);
            if (unwrapped)
                return unwrapped;
        }
        return null;
    }
    if (typeof value === 'string') {
        const parsed = tryParseJsonRecord(value) ?? tryParseJsonRecordFromMaybeSseText(value);
        if (!parsed)
            return null;
        return tryUnwrapKnownShapeFromBody(parsed, depth - 1);
    }
    return null;
}
/**
 * iFlow compatibility: some iFlow backends wrap OpenAI-compatible payloads inside:
 *   { status, msg, body, request_id }
 * where `body` is the actual OpenAI-chat/Responses JSON (or a JSON string).
 *
 * This action unwraps `body` so hub semantic mapping and tool harvesting can proceed.
 */
export function unwrapIflowResponseBodyEnvelope(payload) {
    try {
        if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
            return payload;
        }
        const root = structuredClone(payload);
        // Only unwrap when it looks like the iFlow envelope.
        if (!('body' in root) || !('status' in root) || !('msg' in root)) {
            return root;
        }
        // If the root is already a known provider response, don't touch it.
        if (looksLikeKnownProviderResponseShape(root)) {
            return root;
        }
        const body = root.body;
        const unwrapped = tryUnwrapKnownShapeFromBody(body, 6);
        if (unwrapped) {
            return unwrapped;
        }
        // Fallback: unwrap to `body` even when we can't yet recognize the nested shape.
        // This avoids treating the iFlow envelope as the provider payload and improves
        // downstream diagnostics / error handling.
        if (isRecord(body)) {
            return body;
        }
        if (typeof body === 'string') {
            const parsed = tryParseJsonRecordFromMaybeSseText(body);
            if (parsed) {
                return parsed;
            }
        }
        return root;
    }
    catch {
        return payload;
    }
}
