Source: CmsDataFetching.js

// NOTE This entire functionality seems non-crutial and maintainanece burden for no benefit

/** @typedef {import("./CmsMakeupEngine").ProductUsagePattern} ProductUsagePattern */

/**
 * @typedef {Object} CmsApiConfiguration
 *
 * @property {string} url - base url of api
 * @property {string} accessToken
 * @property {string} languageCode
 */

export class FetchError extends Error {
    /**
     * @param {unknown} cause
     * @param {string} message
     */
    constructor(cause, message = "Fetch failed") {
        super(message);
        this.name = "FetchError";
        this.cause = cause;
    }
}

export class NotFoundError extends Error {
    /**
     * @param {string} entity
     * @param {string | number} id
     */
    constructor(entity, id) {
        super(`${entity} with id ${id} was not found.`);
        this.name = "NotFoundError";
        this.entity = entity;
        this.id = id;
    }
}

/**
 *
 * @param {string} url
 * @returns {Promise<unknown>}
 */
async function fetch_wrap(url) {
    const json = await fetch(url, {
        method: "GET",
        headers: {
            Accept: "application/json",
        },
    });

    return (await json.json()).data;
}

/**
 * @param {CmsApiConfiguration} conf
 * @returns {Promise<unknown>}
 */
async function finishesMany(conf) {
    return fetch_wrap(
        `${conf.url}/finishes?access_token=${conf.accessToken}&language_code=${conf.languageCode}`,
    );
}
/**
 * @param {CmsApiConfiguration} conf
 * @param {number} pup_id
 * @returns {Promise<unknown>}
 */
async function productUsagePatternsSingle(conf, pup_id) {
    return fetch_wrap(
        `${conf.url}/product_usage_patterns/${pup_id}?access_token=${conf.accessToken}&language_code=${conf.languageCode}`,
    );
}
/**
 * @param {CmsApiConfiguration} conf
 * @param {number} product_id
 * @returns {Promise<unknown>}
 */
async function shadeColorsMany(conf, product_id) {
    return fetch_wrap(
        `${conf.url}/products/${product_id}/shade_colors?access_token=${conf.accessToken}&language_code=${conf.languageCode}`,
    );
}
/**
 * @param {CmsApiConfiguration} conf
 * @param {number} pup_id
 * @returns {Promise<unknown>}
 */
async function usagePatternAreasMany(conf, pup_id) {
    return fetch_wrap(
        `${conf.url}/product_usage_patterns/${pup_id}/usage_pattern_areas?access_token=${conf.accessToken}&language_code=${conf.languageCode}`,
    );
}

/**
 * @async
 * @param {Omit<CmsApiConfiguration, "languageCode">} conf
 * @param {number} shade_color_id
 * @returns {Promise<number | undefined>}
 */
export async function tryToFetchshadeColorProductId(conf, shade_color_id) {
    const shadeColors = await fetch_wrap(
        `${conf.url}/shade_colors/${shade_color_id}?access_token=${conf.accessToken}`,
    );

    if (shadeColors instanceof Error) throw shadeColors;

    const shadeColor = /** @type {Array<{product: number | undefined}>} */(shadeColors).at(0);
    if (!shadeColor) throw new NotFoundError("ShadeColors", shade_color_id);

    return shadeColor.product;
}

/**
 * @async
 * @param {CmsApiConfiguration} conf
 * @param {number} pupId
 * @returns {Promise<ProductUsagePattern>}
 */
export async function expandProductUsagePatternFetch(conf, pupId) {
    // prettier-ignore
    const finishProm = finishesMany(conf)
        .catch((reason) => new FetchError(reason, "Couldn't fetch finishes."));
    // prettier-ignore
    const pupProm = productUsagePatternsSingle(conf, pupId)
        .catch((reason) => new FetchError(reason, `Couldn't fetch product usage pattern ${pupId}.`));
    // prettier-ignore
    const upasProm = usagePatternAreasMany(conf, pupId)
        .catch((reason) => new FetchError(reason, `Couldn't fetch usage pattern arreas for product usage pattern ${pupId}.`));

    const [finishes, pups, upas] = await Promise.all([
        finishProm,
        pupProm,
        upasProm,
    ]);
    if (finishes instanceof Error) throw finishes; //TODO Appropriate error message and documented
    if (pups instanceof Error) throw pups; //TODO Appropriate error message and documented
    if (upas instanceof Error) throw upas; //TODO Appropriate error message and documented

    const pup = pups.at(0);
    if (!pup) throw new NotFoundError("ProductUsagePattern", pupId);

    // prettier-ignore
    const shadeProm = shadeColorsMany(conf, pup.product)
        .catch(
            (reason) => new FetchError(reason, `Couldn't fetch shadecolors for product ${pup.product}.`)
        );

    const [shades] = await Promise.all([shadeProm]);
    if (shades instanceof Error) throw shades; //TODO Appropriate error message and documented

    return {
        id: pup.id,
        product: pup.product,
        usagePatternAreas: upas.map((upa) => {
            const shadeColor = shades.find((s) => s.id === upa.shadeColor);
            if (!shadeColor)
                throw new NotFoundError("ShadeColor", upa.shadeColor);

            const finish = finishes.find((f) => f.id === shadeColor.finish);
            if (!finish) throw new NotFoundError("Finish", shadeColor.finish);

            return {
                applicationArea: upa.applicationArea,
                shadeColor: {
                    id: shadeColor.id,
                    colorOpacity: shadeColor.colorOpacity,
                    finishOpacity: shadeColor.finishOpacity,
                    rendering: shadeColor.rendering,
                    finish: {
                        name: finish.name,
                    },
                },
            };
        }),
    };
}