/* eslint-disable */
/* tslint:disable */

/**
 * Mock Service Worker.
 * @see https://github.com/mswjs/msw
 * - Please do NOT modify this file.
 */

const PACKAGE_VERSION = "2.12.3";
const INTEGRITY_CHECKSUM = "4db4a41e972cec1b64cc569c66952d82";
const IS_MOCKED_RESPONSE = Symbol("isMockedResponse");
const activeClientIds = new Set();

addEventListener("install", function () {
    self.skipWaiting();
});

addEventListener("activate", function (event) {
    event.waitUntil(self.clients.claim());
});

addEventListener("message", async function (event) {
    const clientId = Reflect.get(event.source || {}, "id");

    if (!clientId || !self.clients) {
        return;
    }

    const client = await self.clients.get(clientId);

    if (!client) {
        return;
    }

    const allClients = await self.clients.matchAll({
        type: "window",
    });

    switch (event.data) {
        case "KEEPALIVE_REQUEST": {
            sendToClient(client, {
                type: "KEEPALIVE_RESPONSE",
            });
            break;
        }

        case "INTEGRITY_CHECK_REQUEST": {
            sendToClient(client, {
                type: "INTEGRITY_CHECK_RESPONSE",
                payload: {
                    packageVersion: PACKAGE_VERSION,
                    checksum: INTEGRITY_CHECKSUM,
                },
            });
            break;
        }

        case "MOCK_ACTIVATE": {
            activeClientIds.add(clientId);

            sendToClient(client, {
                type: "MOCKING_ENABLED",
                payload: {
                    client: {
                        id: client.id,
                        frameType: client.frameType,
                    },
                },
            });
            break;
        }

        case "CLIENT_CLOSED": {
            activeClientIds.delete(clientId);

            const remainingClients = allClients.filter(client => {
                return client.id !== clientId;
            });

            // Unregister itself when there are no more clients
            if (remainingClients.length === 0) {
                self.registration.unregister();
            }

            break;
        }
    }
});

addEventListener("fetch", function (event) {
    const requestInterceptedAt = Date.now();

    // Bypass navigation requests.
    if (event.request.mode === "navigate") {
        return;
    }

    // Opening the DevTools triggers the "only-if-cached" request
    // that cannot be handled by the worker. Bypass such requests.
    if (event.request.cache === "only-if-cached" && event.request.mode !== "same-origin") {
        return;
    }

    // Bypass all requests when there are no active clients.
    // Prevents the self-unregistered worked from handling requests
    // after it's been terminated (still remains active until the next reload).
    if (activeClientIds.size === 0) {
        return;
    }

    const requestId = crypto.randomUUID();
    event.respondWith(handleRequest(event, requestId, requestInterceptedAt));
});

/**
 * @param {FetchEvent} event
 * @param {string} requestId
 * @param {number} requestInterceptedAt
 */
async function handleRequest(event, requestId, requestInterceptedAt) {
    const client = await resolveMainClient(event);
    const requestCloneForEvents = event.request.clone();
    const response = await getResponse(event, client, requestId, requestInterceptedAt);

    // Send back the response clone for the "response:*" life-cycle events.
    // Ensure MSW is active and ready to handle the message, otherwise
    // this message will pend indefinitely.
    if (client && activeClientIds.has(client.id)) {
        const serializedRequest = await serializeRequest(requestCloneForEvents);

        // Clone the response so both the client and the library could consume it.
        const responseClone = response.clone();

        sendToClient(
            client,
            {
                type: "RESPONSE",
                payload: {
                    isMockedResponse: IS_MOCKED_RESPONSE in response,
                    request: {
                        id: requestId,
                        ...serializedRequest,
                    },
                    response: {
                        type: responseClone.type,
                        status: responseClone.status,
                        statusText: responseClone.statusText,
                        headers: Object.fromEntries(responseClone.headers.entries()),
                        body: responseClone.body,
                    },
                },
            },
            responseClone.body ? [serializedRequest.body, responseClone.body] : []
        );
    }

    return response;
}

/**
 * Resolve the main client for the given event.
 * Client that issues a request doesn't necessarily equal the client
 * that registered the worker. It's with the latter the worker should
 * communicate with during the response resolving phase.
 * @param {FetchEvent} event
 * @returns {Promise<Client | undefined>}
 */
async function resolveMainClient(event) {
    const client = await self.clients.get(event.clientId);

    if (activeClientIds.has(event.clientId)) {
        return client;
    }

    if (client?.frameType === "top-level") {
        return client;
    }

    const allClients = await self.clients.matchAll({
        type: "window",
    });

    return allClients
        .filter(client => {
            // Get only those clients that are currently visible.
            return client.visibilityState === "visible";
        })
        .find(client => {
            // Find the client ID that's recorded in the
            // set of clients that have registered the worker.
            return activeClientIds.has(client.id);
        });
}

/**
 * @param {FetchEvent} event
 * @param {Client | undefined} client
 * @param {string} requestId
 * @param {number} requestInterceptedAt
 * @returns {Promise<Response>}
 */
async function getResponse(event, client, requestId, requestInterceptedAt) {
    // Clone the request because it might've been already used
    // (i.e. its body has been read and sent to the client).
    const requestClone = event.request.clone();

    function passthrough() {
        // Cast the request headers to a new Headers instance
        // so the headers can be manipulated with.
        const headers = new Headers(requestClone.headers);

        // Remove the "accept" header value that marked this request as passthrough.
        // This prevents request alteration and also keeps it compliant with the
        // user-defined CORS policies.
        const acceptHeader = headers.get("accept");
        if (acceptHeader) {
            const values = acceptHeader.split(",").map(value => value.trim());
            const filteredValues = values.filter(value => value !== "msw/passthrough");

            if (filteredValues.length > 0) {
                headers.set("accept", filteredValues.join(", "));
            } else {
                headers.delete("accept");
            }
        }

        return fetch(requestClone, { headers });
    }

    // Bypass mocking when the client is not active.
    if (!client) {
        return passthrough();
    }

    // Bypass initial page load requests (i.e. static assets).
    // The absence of the immediate/parent client in the map of the active clients
    // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
    // and is not ready to handle requests.
    if (!activeClientIds.has(client.id)) {
        return passthrough();
    }

    // Notify the client that a request has been intercepted.
    const serializedRequest = await serializeRequest(event.request);
    const clientMessage = await sendToClient(
        client,
        {
            type: "REQUEST",
            payload: {
                id: requestId,
                interceptedAt: requestInterceptedAt,
                ...serializedRequest,
            },
        },
        [serializedRequest.body]
    );

    switch (clientMessage.type) {
        case "MOCK_RESPONSE": {
            return respondWithMock(clientMessage.data);
        }

        case "PASSTHROUGH": {
            return passthrough();
        }
    }

    return passthrough();
}

/**
 * @param {Client} client
 * @param {any} message
 * @param {Array<Transferable>} transferrables
 * @returns {Promise<any>}
 */
function sendToClient(client, message, transferrables = []) {
    return new Promise((resolve, reject) => {
        const channel = new MessageChannel();

        channel.port1.onmessage = event => {
            if (event.data && event.data.error) {
                return reject(event.data.error);
            }

            resolve(event.data);
        };

        client.postMessage(message, [channel.port2, ...transferrables.filter(Boolean)]);
    });
}

/**
 * @param {Response} response
 * @returns {Response}
 */
function respondWithMock(response) {
    // Setting response status code to 0 is a no-op.
    // However, when responding with a "Response.error()", the produced Response
    // instance will have status code set to 0. Since it's not possible to create
    // a Response instance with status code 0, handle that use-case separately.
    if (response.status === 0) {
        return Response.error();
    }

    const mockedResponse = new Response(response.body, response);

    Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
        value: true,
        enumerable: true,
    });

    return mockedResponse;
}

/**
 * @param {Request} request
 */
async function serializeRequest(request) {
    return {
        url: request.url,
        mode: request.mode,
        method: request.method,
        headers: Object.fromEntries(request.headers.entries()),
        cache: request.cache,
        credentials: request.credentials,
        destination: request.destination,
        integrity: request.integrity,
        redirect: request.redirect,
        referrer: request.referrer,
        referrerPolicy: request.referrerPolicy,
        body: await request.arrayBuffer(),
        keepalive: request.keepalive,
    };
}
