import React, { lazy } from 'react';

const isChunkLoadError = (error: unknown): boolean => {
    if (!(error instanceof Error)) {
        return false;
    }

    const message = error.message.toLowerCase();

    return (
        error.name === 'ChunkLoadError' ||
        message.includes('chunkloaderror') ||
        message.includes('loading chunk') ||
        message.includes('failed to fetch dynamically imported module') ||
        message.includes('importing a module script failed') ||
        message.includes('module script') ||
        message.includes('dynamically imported module')
    );
};

const getRetryKey = (): string => {
    try {
        return `lazy-retry-reload:${window.location.pathname}`;
    } catch {
        return 'lazy-retry-reload';
    }
};

const hasRetried = (): boolean => {
    try {
        return window.sessionStorage.getItem(getRetryKey()) === 'true';
    } catch {
        return false;
    }
};

const markAsRetried = (): void => {
    try {
        window.sessionStorage.setItem(getRetryKey(), 'true');
    } catch {
        // Ignore storage errors.
    }
};

const clearRetryFlag = (): void => {
    try {
        window.sessionStorage.removeItem(getRetryKey());
    } catch {
        // Ignore storage errors.
    }
};

const lazyWithRetry = (
    componentImport: () => Promise<{
        default: React.ComponentType<any>;
    }>
) =>
    lazy(async () => {
        try {
            const component = await componentImport();

            clearRetryFlag();

            return component;
        } catch (error) {
            if (!isChunkLoadError(error)) {
                throw error;
            }

            if (hasRetried()) {
                clearRetryFlag();
                throw error;
            }

            markAsRetried();

            window.location.reload();

            // This should never actually render because reload() is triggered.
            // Throw instead of returning a fake component.
            throw new Error(
                'Application is reloading after a chunk load failure.'
            );
        }
    });

export default lazyWithRetry;
