import { backOff } from 'exponential-backoff';
import { DisconnectedError, UnauthorizedTokenError } from '../utils/errors.js';
import { clone } from '../utils/utils.js';
import { canBeFormUrlEncoded, encodeAsFormUrl } from './form-url-encoder.js';
function isThrottled(status) {
    return status === 429;
}
// biome-ignore lint/complexity/noStaticOnlyClass: Maybe change this into a function someday. Not worth the effort right now.
export class PlatformClient {
    static async call(options) {
        const defaultRequestOptions = buildDefaultRequestOptions(options);
        const { logger } = options;
        const requestInfo = await PlatformClient.preprocessRequest(defaultRequestOptions, options);
        logger.info(requestInfo, 'Platform request');
        const { url, ...requestData } = requestInfo;
        const request = async () => {
            const response = await fetch(url, requestData);
            if (isThrottled(response.status)) {
                throw response;
            }
            return response;
        };
        try {
            const response = await backOff(request, {
                retry: (e) => {
                    const shouldRetry = e && isThrottled(e.status);
                    shouldRetry && logger.info('Platform retrying request');
                    return shouldRetry;
                },
            });
            switch (response.status) {
                case 419:
                case 401:
                    logger.info('Platform renewing token');
                    throw new UnauthorizedTokenError();
                case 404:
                    throw new DisconnectedError(url, response.status);
                default:
                    logger.info({ response, requestInfo }, 'Platform response');
                    return response;
            }
        }
        catch (error) {
            if (error.message === 'Failed to fetch') {
                return new DisconnectedError(url);
            }
            return error;
        }
    }
    static async preprocessRequest(defaultRequestOptions, options) {
        const { origin, preprocessRequest, logger, requestMetadata } = options;
        const { signal: _signal, ...withoutSignal } = defaultRequestOptions;
        const untaintedOutput = clone(withoutSignal);
        try {
            const processedRequest = await preprocessRequest(defaultRequestOptions, origin, requestMetadata);
            return {
                ...defaultRequestOptions,
                ...processedRequest,
            };
        }
        catch (e) {
            logger.error(e, 'Platform request preprocessing failed. Returning default request options.');
        }
        return untaintedOutput;
    }
}
/**
 * Retrieves the endpoint URL for a specific organization.
 *
 * @param organizationId - The ID of the organization.
 * @param environment - The environment of the organization. Defaults to 'prod'.
 * @param endpointType - The type of the endpoint. Defaults to 'platform'.
 * @returns The endpoint URL for the organization.
 */
export function getOrganizationEndpoint(organizationId, environment = 'prod', endpointType = 'platform') {
    const environmentSuffix = environment === 'prod' ? '' : environment;
    const endpointTypePart = endpointType === 'platform' ? '' : `.${endpointType}`;
    return `https://${organizationId}${endpointTypePart}.org${environmentSuffix}.coveo.com`;
}
export function getSearchApiBaseUrl(organizationId, environment = 'prod') {
    const organizationEndpoint = getOrganizationEndpoint(organizationId, environment);
    return `${organizationEndpoint}/rest/search/v2`;
}
export function getAnalyticsNextApiBaseUrl(organizationId, environment = 'prod') {
    const organizationEndpoint = getOrganizationEndpoint(organizationId, environment, 'analytics');
    return `${organizationEndpoint}/rest/organizations/${organizationId}/events/v1`;
}
function buildDefaultRequestOptions(options) {
    const { url, method, requestParams, contentType, accessToken, signal } = options;
    const isMethodWithBody = options.method === 'POST' || options.method === 'PUT';
    const body = encodeBody(requestParams, contentType);
    return {
        url,
        method,
        headers: {
            'Content-Type': contentType,
            Authorization: `Bearer ${accessToken}`,
            ...options.headers,
        },
        ...(isMethodWithBody && { body }),
        signal,
    };
}
function encodeBody(body, contentType) {
    if (contentType === 'application/x-www-form-urlencoded') {
        return canBeFormUrlEncoded(body) ? encodeAsFormUrl(body) : '';
    }
    return JSON.stringify(body);
}
