function isReadable(v) {
    return !!v && typeof v.pipe === 'function';
}
function setSSEHeadersRaw(res) {
    res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
    res.setHeader('Cache-Control', 'no-cache, no-transform');
    res.setHeader('Connection', 'keep-alive');
}
/**
 * Pipe an SSE Readable to Node http.ServerResponse
 */
export function sendNodeSSE(res, stream) {
    setSSEHeadersRaw(res);
    // Write initial comment to establish stream in some proxies
    try {
        res.write(': ok\n\n');
    }
    catch { /* ignore */ }
    stream.on('data', (chunk) => {
        res.write(typeof chunk === 'string' ? chunk : chunk.toString());
    });
    stream.on('end', () => {
        try {
            res.write(':\n\n');
        }
        catch { /* ignore */ }
        res.end();
    });
    stream.on('error', (err) => {
        try {
            res.write(`event: error\n`);
            res.write(`data: ${JSON.stringify({ message: String(err?.message || 'sse_error') })}\n\n`);
        }
        catch { /* ignore */ }
        res.end();
    });
}
/**
 * Express response variant (duck-typed): res.set, res.write, res.end
 */
export function sendExpressSSE(res, stream) {
    if (typeof res.set === 'function') {
        res.set('Content-Type', 'text/event-stream; charset=utf-8');
        res.set('Cache-Control', 'no-cache, no-transform');
        res.set('Connection', 'keep-alive');
    }
    else if (typeof res.setHeader === 'function') {
        setSSEHeadersRaw(res);
    }
    try {
        res.write(': ok\n\n');
    }
    catch { /* ignore */ }
    stream.on('data', (chunk) => res.write(typeof chunk === 'string' ? chunk : chunk.toString()));
    stream.on('end', () => res.end());
    stream.on('error', (err) => {
        try {
            res.write(`event: error\n`);
            res.write(`data: ${JSON.stringify({ message: String(err?.message || 'sse_error') })}\n\n`);
        }
        catch { /* ignore */ }
        res.end();
    });
}
/**
 * Fastify reply variant: reply.raw is ServerResponse
 */
export function sendFastifySSE(reply, stream) {
    const raw = (reply && reply.raw);
    if (raw)
        return sendNodeSSE(raw, stream);
    // fallback to express-like
    return sendExpressSSE(reply, stream);
}
/**
 * If the given payload contains __sse_responses or sseStream, stream it via the provided response object.
 * Otherwise, send JSON.
 * - Supported responders: Node ServerResponse, Express res, Fastify reply
 */
export function sendSSEOrJSON(resOrReply, payload) {
    const stream = payload.__sse_responses || payload.sseStream;
    if (isReadable(stream)) {
        // detect responder flavor
        if (typeof resOrReply.raw !== 'undefined')
            return sendFastifySSE(resOrReply, stream);
        if (typeof resOrReply.setHeader === 'function')
            return sendNodeSSE(resOrReply, stream);
        return sendExpressSSE(resOrReply, stream);
    }
    // Fallback JSON
    const jsonTxt = JSON.stringify(payload);
    if (typeof resOrReply.json === 'function')
        return resOrReply.json(payload);
    if (typeof resOrReply.send === 'function')
        return resOrReply.send(jsonTxt);
    if (typeof resOrReply.end === 'function') {
        try {
            resOrReply.setHeader?.('Content-Type', 'application/json; charset=utf-8');
        }
        catch { /* ignore */ }
        return resOrReply.end(jsonTxt);
    }
}
