// (C) 2019-2025 GoodData Corporation
import { merge, uniqBy } from "lodash-es";
import { jsonApiHeaders } from "./constants.js";
const DefaultPageSize = 250;
const DefaultOptions = {
    headers: jsonApiHeaders,
    params: {
        size: DefaultPageSize,
    },
};
function createOptionsForPage(page, options) {
    return merge({}, DefaultOptions, options, { params: { page } });
}
/**
 * Tiger metadata utility functions
 *
 * @internal
 */
export class MetadataUtilities {
    /**
     * Given a function to get a paged list of metadata entities, API call parameters and options, this function will
     * retrieve all pages from the metadata.
     *
     * The parameters are passed to the function as is. The options will be used as a 'template'. If the options specify
     * page `size`, it will be retained and used for paging. Otherwise, the size will be set to a default value (250). The
     * `page` number will be added dynamically upon each page request.
     *
     * @param client - API client to use, this is required so that function can correctly bind 'this' for
     *  the entitiesGet function
     * @param entitiesGet - function to get pages list of entities
     * @param params - parameters accepted by the function
     * @param options - options accepted by the function
     * @internal
     */
    /*public static getAllPagesOf = async <
        T extends MetadataGetEntitiesResult,
        P extends MetadataGetEntitiesParams,
    >(
        client: ITigerClient,
        entitiesGet: MetadataGetEntitiesFn<T, P>,
        params: P,
        options: MetadataGetEntitiesOptions = {},
    ): Promise<T[]> => {
        const boundGet = entitiesGet.bind(client.entities);
        const results: T[] = [];
        const pageSize = options.params?.size ?? DefaultPageSize;
        let reachedEnd = false;
        let nextPage: number = 0;

        while (!reachedEnd) {
            const optionsToUse = createOptionsForPage(nextPage, options);
            const result = await boundGet(params, optionsToUse);

            results.push(result.data);

            if (result.data.data.length < pageSize) {
                reachedEnd = true;
            } else {
                nextPage += 1;
            }
        }

        return results;
    };*/
    /**
     * Given a function to get a paged list of metadata entities, API call parameters and options, this function will
     * retrieve all pages from the metadata.
     * Unlike to the getAllPagesOf, the requests will not be done in series. Instead, a first request will be done
     * with metaInclude=page to get the number of pages and all subsequent pages will be fetched in parallel.
     *
     * The parameters are passed to the function as is. The options will be used as a 'template'. If the options specify
     * page `size`, it will be retained and used for paging.
     *
     * @param client - API client to use, this is required so that function can correctly bind 'this' for
     *  the entitiesGet function
     * @param entitiesGet - function to get pages list of entities
     * @param params - parameters accepted by the function
     * @param options - options accepted by the function
     * @internal
     */
    /* public static getAllPagesOfParallel = async <
        T extends MetadataGetEntitiesResult,
        P extends MetadataGetEntitiesParams,
    >(
        client: ITigerClient,
        entitiesGet: MetadataGetEntitiesFn<T, P>,
        params: P,
        options: MetadataGetEntitiesOptions = {},
    ): Promise<T[]> => {
        const boundGet = entitiesGet.bind(client.entities);

        const initialOptions = createOptionsForPage(0, options);
        const initialGet = await boundGet({ ...params, metaInclude: ["page"] }, initialOptions);

        const total = initialGet.data?.meta?.page?.totalPages || 1;

        const range = Array.from({ length: total - 1 }, (_v, i) => i + 1);

        const morePages = await Promise.all(
            range.map((page) => {
                const optionsToUse = createOptionsForPage(page, options);
                return boundGet(params, optionsToUse);
            }),
        );

        return [initialGet, ...morePages].map((item) => item.data);
    }; */
    /**
     * This function merges multiple pages containing metadata entities into a single page. The entity data from different
     * pages are concatenated. The side-loaded entities are concatenated and their uniqueness is ensured so that same
     * entity side-loaded on multiple pages only appears once.
     *
     * The merges result WILL NOT contain any links section.
     *
     * @param pages - pages to merge
     * @internal
     */
    static mergeEntitiesResults(pages) {
        return {
            data: pages.flatMap((page) => page.data),
            included: uniqBy(
            // we need the as any because the JsonApiDashboardPluginOutList does not have the "included" property
            pages.flatMap((page) => page.included ?? []), (item) => `${item.id}_${item.type}`),
        };
    }
    /**
     * Given list of JSON API entities, return those which have not broken relations to other MD objects. This
     * info is computed by backend when "X-GDC-VALIDATE-RELATIONS" is sent with the GET request. Note that backend
     * checks the relations recursively.
     *
     * @param result - MetadataGetEntitiesResult
     */
    static filterValidEntities(result) {
        result.data = result.data.filter((entity) => entity.attributes?.areRelationsValid ?? true);
        return result;
    }
    static getAllPagesOf = async (client, entitiesGet, params, options = {}) => {
        const results = [];
        const pageSize = options.params?.size ?? DefaultPageSize;
        let reachedEnd = false;
        let nextPage = 0;
        while (!reachedEnd) {
            const optionsToUse = createOptionsForPage(nextPage, options);
            const result = await entitiesGet(client.axios, "", params, optionsToUse);
            results.push(result.data);
            if (result.data.data.length < pageSize) {
                reachedEnd = true;
            }
            else {
                nextPage += 1;
            }
        }
        return results;
    };
    /**
     * Given a function to get a paged list of metadata entities, API call parameters and options, this function will
     * retrieve all pages from the metadata.
     * Unlike to the getAllPagesOf, the requests will not be done in series. Instead, a first request will be done
     * with metaInclude=page to get the number of pages and all subsequent pages will be fetched in parallel.
     *
     * The parameters are passed to the function as is. The options will be used as a 'template'. If the options specify
     * page `size`, it will be retained and used for paging.
     *
     * @param client - API client to use, this is required so that function can correctly bind 'this' for
     *  the entitiesGet function
     * @param entitiesGet - function to get pages list of entities
     * @param params - parameters accepted by the function
     * @param options - options accepted by the function
     * @internal
     */
    static getAllPagesOfParallel = async (client, entitiesGet, params, options = {}) => {
        const initialOptions = createOptionsForPage(0, options);
        const initialGet = await entitiesGet(client.axios, "", { ...params, metaInclude: ["page"] }, initialOptions);
        const total = initialGet.data?.meta?.page?.totalPages || 1;
        const range = Array.from({ length: total - 1 }, (_v, i) => i + 1);
        const morePages = await Promise.all(range.map((page) => {
            const optionsToUse = createOptionsForPage(page, options);
            return entitiesGet(client.axios, "", params, optionsToUse);
        }));
        return [initialGet, ...morePages].map((item) => item.data);
    };
}
//# sourceMappingURL=metadataUtilities.js.map