import { APP_NAME } from '../../config.js';
import { SHORTCUT_LLM_PROXY_ENDPOINTS } from '../../endpoints.js';
import { SHORTCUT_PROVIDER_ID } from '../../model-ids.js';
export class ShortcutLlmProxyError extends Error {
    code;
    status;
    bodyPreview;
    constructor(code, message, options = {}) {
        super(message, options.cause !== undefined ? { cause: options.cause } : undefined);
        this.name = 'ShortcutLlmProxyError';
        this.code = code;
        this.status = options.status;
        this.bodyPreview = options.bodyPreview;
    }
}
/**
 * Convert proxy transport errors into user-facing errors expected by tools.
 *
 * Callers should catch and map only at their API boundary so lower layers can
 * still inspect ShortcutLlmProxyError.code/status for diagnostics and tests.
 */
export function mapShortcutLlmProxyError(error) {
    if (error instanceof ShortcutLlmProxyError) {
        if (error.code === 'missing-auth') {
            return new Error('Not authenticated. Please log in first.');
        }
        if (error.code === 'network-error') {
            return new Error(error.message);
        }
        if (error.code === 'http-error') {
            return new Error(`LLM proxy error ${error.status}: ${error.bodyPreview ?? ''}`);
        }
    }
    return error instanceof Error ? error : new Error(String(error));
}
function normalizeAttribution(attribution) {
    if (!attribution?.sessionId && !attribution?.userMessageId)
        return undefined;
    return attribution;
}
function getProxyBaseUrl(modelRegistry) {
    return modelRegistry
        .getAll()
        .find((model) => model.provider === SHORTCUT_PROVIDER_ID)
        ?.baseUrl?.replace(/\/+$/, '');
}
function buildRequestBody(request, attribution) {
    // The proxy expects session attribution split between transport metadata
    // (X-Session-Id) and model request metadata (user_message_id).
    return {
        ...request,
        ...(attribution?.userMessageId ? { user_message_id: attribution.userMessageId } : {})
    };
}
/** Read a bounded error body for diagnostics without logging huge model responses. */
async function readBodyPreview(response) {
    const raw = await response.text();
    return raw.split('\n').slice(0, 10).join('\n');
}
/**
 * Create a Shortcut LLM proxy client backed by the current model registry.
 *
 * The returned methods intentionally share requestProxy: invoke() only adds JSON
 * parsing, while stream() returns the successful streaming Response unchanged.
 */
export function createShortcutLlmProxyClient(options) {
    /**
     * Shared POST path for all proxy endpoints.
     *
     * This helper keeps aborts transparent, wraps network/configuration failures
     * in ShortcutLlmProxyError, and preserves HTTP status/body previews for
     * downstream diagnostics.
     */
    const requestProxy = async (endpoint, request, signal) => {
        const apiKey = await options.modelRegistry.getApiKeyForProvider(SHORTCUT_PROVIDER_ID);
        if (!apiKey) {
            throw new ShortcutLlmProxyError('missing-auth', 'Not authenticated. Please log in first.');
        }
        const baseUrl = getProxyBaseUrl(options.modelRegistry);
        if (!baseUrl) {
            throw new ShortcutLlmProxyError('missing-base-url', 'Shortcut LLM proxy base URL is not configured.');
        }
        const attribution = normalizeAttribution(options.getAttribution?.());
        const body = buildRequestBody(request, attribution);
        let response;
        try {
            response = await fetch(`${baseUrl}${endpoint}`, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'X-Client-Platform': 'shortcutxl',
                    ...(attribution?.sessionId ? { 'X-Session-Id': attribution.sessionId } : {}),
                    Authorization: `Bearer ${apiKey}`,
                    'User-AgentController': `Mozilla/5.0 (compatible; ${APP_NAME}-coding-agent/1.0)`
                },
                body: JSON.stringify(body),
                signal
            });
        }
        catch (error) {
            if (signal?.aborted || (error instanceof Error && error.name === 'AbortError')) {
                throw error;
            }
            const msg = error instanceof Error ? error.message : String(error);
            throw new ShortcutLlmProxyError('network-error', `Failed to connect to LLM proxy: ${msg}`, {
                cause: error
            });
        }
        if (!response.ok) {
            const bodyPreview = await readBodyPreview(response);
            throw new ShortcutLlmProxyError('http-error', `LLM proxy error ${response.status}: ${bodyPreview}`, {
                status: response.status,
                bodyPreview
            });
        }
        return response;
    };
    return {
        async invoke(request, options) {
            const response = await requestProxy(SHORTCUT_LLM_PROXY_ENDPOINTS.invoke, request, options?.signal);
            try {
                return (await response.json());
            }
            catch (error) {
                throw new ShortcutLlmProxyError('invalid-json', 'LLM proxy returned invalid JSON.', {
                    cause: error
                });
            }
        },
        stream(request, options) {
            return requestProxy(SHORTCUT_LLM_PROXY_ENDPOINTS.stream, request, options?.signal);
        }
    };
}
//# sourceMappingURL=shortcut-llm-proxy-client.js.map