import {AppProvider} from "../contexts/AppContext";
import {useContextMenu} from "react-contexify";
import Header from "./App/Header";
import Sidebar from "./App/Sidebar";
import Body from "./App/Body";
import Details from "./App/Details";
import ContextMenu from "./ContextMenu/ContextMenu";
import AddAccountPlaceholder from "./App/placeholder/AddAccountPlaceholder";

import {
    getListingView,
    getRootFolders,
    isAudioVideoType,
    isFolder,
    isImageType,
    isRootFolder,
    isVideoType,
    getSessionLastFolder,
    removeLastFolderSession,
    setLastFolderSession,
    useMounted,
    sortFiles,
} from "../includes/functions";

const {useState, useEffect, useMemo, useRef} = React;

const {settings, isAdmin, userAccessData} = igd;

let {
    rememberLastFolder = isAdmin,
    adminLazyLoad = true,
    adminLazyLoadType = 'pagination',
    adminLazyLoadNumber = 100,
    asyncLoading,
} = settings;

export default function App({
                                shortcodeId,
                                nonce,

                                initParentFolder,
                                isShortcodeBuilder,
                                selectedFolders = [],
                                setSelectedFolders,
                                allFolders,
                                privateFolders,
                                initFolders,
                                filters,
                                shortcodeBuilderType,
                                isList: initIsList,
                                showLastModified = !shortcodeBuilderType,
                                showFileSizeField = true,
                                fileNumbers,
                                sort: initSort,
                                initialFilesSorting,
                                permissions,
                                notifications,
                                showHeader = isShortcodeBuilder || !shortcodeBuilderType,
                                showRefresh = true,
                                showSorting = true,
                                showFilesCount = true,
                                showBreadcrumbs = true,
                                searchBoxText,

                                galleryLayout,
                                galleryAspectRatio,
                                galleryColumns,
                                galleryHeight,
                                galleryMargin,
                                galleryView,
                                galleryFolderView,
                                thumbnailCaption,
                                galleryOverlay,
                                overlayDisplayType,
                                galleryOverlayTitle,
                                galleryOverlayDescription,
                                galleryOverlaySize,
                                galleryImageSize,
                                galleryCustomSizeWidth,
                                galleryCustomSizeHeight,

                                isSelectFiles,
                                selectionType,
                                initialSearchTerm,
                                isLMS,
                                isWooCommerce,
                                lazyLoad = adminLazyLoad,
                                lazyLoadNumber = adminLazyLoadNumber,
                                lazyLoadType = adminLazyLoadType,
                                accounts = igd.accounts,
                                account = igd.activeAccount,
                                selection,
                                isFormUploader,
                                uploadFileName = '%file_name%%file_extension%',
                                overwrite,
                            }) {

    const [activeAccount, setActiveAccount] = useState(account);

    const isSpecificFolders = 'specific_folders' === activeAccount.scope;


    if (!initFolders) {

        if (userAccessData) {
            initParentFolder = userAccessData.initParentFolder;
            initFolders = userAccessData.initFolders;
        } else {

            if (allFolders) {
                initFolders = getRootFolders(null, activeAccount);
            }

        }

    }

    const isSearch = 'search' === shortcodeBuilderType && !isShortcodeBuilder;
    const isGallery = 'gallery' === shortcodeBuilderType;
    const isMedia = 'media' === shortcodeBuilderType;
    const isUploader = 'uploader' === shortcodeBuilderType;
    const isReview = 'review' === shortcodeBuilderType;

    // Handle remember last folder
    let lastFolder = null;
    if (!isSearch && rememberLastFolder && (!initFolders || !asyncLoading)) {
        lastFolder = getSessionLastFolder(shortcodeId, initParentFolder, activeAccount.id);

        // latest changes compatibility
        if (lastFolder && lastFolder.id === 'root') {
            lastFolder.id = activeAccount.root_id;
        }

    }

    const [files, setFiles] = useState(!isSearch && !!initFolders ? initFolders : []);
    const [page, setPage] = useState(1);

    let initAllFiles = [];
    if (!isSearch && initFolders) {
        initAllFiles = {[initParentFolder?.id || '']: initFolders};
    }

    const [allFiles, setAllFiles] = useState(initAllFiles);

    const ignoreResetSelections = isReview || (isGallery && permissions?.photoProof);
    const [activeFiles, setActiveFiles] = useState([]);

    const [activeFile, setActiveFile] = useState(null);
    const [isLoading, setIsLoading] = useState((!initFolders && !initParentFolder) && !isSearch && !isSpecificFolders);
    const [breadcrumbs, setBreadcrumbs] = useState({});
    const [isSearchResults, setIsSearchResults] = useState(false);
    const [isUpload, setIsUpload] = useState(false);
    const [isMobile, setIsMobile] = useState(window.innerWidth <= 768);
    const [isOptions, setIsOptions] = useState(false);
    const [isBulkSelect, setIsBulkSelect] = useState(false);
    const [selectAll, setSelectAll] = useState(false);

    const searchKeywordRef = useRef(initialSearchTerm);
    const [searchKeyword, setSearchKeyword] = useState(initialSearchTerm);
    const [searchResults, setSearchResults] = useState(null);

    // Handle initializes show details
    const [showDetails, setShowDetails] = useState(() => {
        if (isMobile || initFolders) return false;

        return localStorage.getItem('igd_show_details') === '1';
    });


    // Handle initializes show sidebar
    const [showSidebar, setShowSidebar] = useState(() => {
        if (isMobile || initFolders) return false;

        return localStorage.getItem('igd_file_browser_sidebar_open') !== '0';

    });

    // Handle initialize active root and folder
    let initActiveFolder = null;

    if (initFolders) {
        if (initParentFolder) {
            initActiveFolder = initParentFolder;
        }
    } else {
        if (!isSpecificFolders) {
            initActiveFolder = getRootFolders(activeAccount.root_id, activeAccount);
        }
    }

    if (lastFolder) {
        initActiveFolder = lastFolder;
    }

    const [activeFolder, setActiveFolder] = useState(initActiveFolder);

    // Default sort config
    const defaultSort = {
        sortBy: 'name',
        sortDirection: 'asc',
    };

    // Load saved sort (if any)
    const storedSort = JSON.parse(localStorage.getItem('igd_sort') || 'null');

    // Decide initial sort value
    const initialSort =
        !storedSort && initFolders && !activeFolder && !initialFilesSorting
            ? false
            : initSort ?? storedSort ?? defaultSort;

    const [sort, setSort] = useState(initialSort);

    // View
    let savedView = getListingView(shortcodeId || 'admin');

    if (savedView) {
        initIsList = savedView === 'list';
    }

    const [isList, setIsList] = useState(initIsList);

    function showSwalError(message) {
        if (document.querySelector('.igd-swal')) {
            Swal.showValidationMessage(message);
            return;
        }

        Swal.fire({
            html: message,
            icon: 'error',
            confirmButtonText: wp.i18n.__('Ok', 'integrate-google-drive'),
            customClass: {container: 'igd-swal'},
        });
    }

    function cleanupBreadcrumbs({breadcrumbs, folder, initFolders, initParentFolder, isSearch, activeFolder}) {
        let cleaned = {...breadcrumbs};
        const keys = Object.keys(cleaned);

        if (!keys.length) {
            return cleaned;
        }

        // Case: init parent folder exists
        if (initParentFolder?.id) {
            if (keys.includes(initParentFolder.id)) {
                delete cleaned[initParentFolder.id];
            }

            if (keys[keys.length - 1] === initParentFolder.id) {
                return {};
            }

            return cleaned;
        }

        // Case: normal navigation
        const activeId = folder?.id || activeFolder;
        const activeIndex = keys.indexOf(activeId);

        if (activeIndex === -1) {
            return cleaned;
        }

        for (let i = 0; i < activeIndex; i++) {
            if (!initFolders.find(f => f.id === keys[i])) {
                delete cleaned[keys[i]];
            } else {
                // IMPORTANT: search behavior must stay intact
                if (isSearch) {
                    delete cleaned[keys[i]];
                }
                break;
            }
        }

        return cleaned;
    }

    function getRequestFilters() {
        return {
            ...(filters || {}),
            ...(isGallery && {isGallery: true}),
            ...(isMedia && {isMedia: true}),
            ...(isLMS && {onlyVideo: true}),
            ...((isUploader || selectionType === 'folders') && {onlyFolders: true}),
            ...(shortcodeBuilderType === 'table' && {onlyTables: true}),
        };
    }

    function filterFiles(items) {
        return items.filter(item => {
            if ((isUploader || selectionType === 'folders') && !isFolder(item)) return false;
            if (isMedia && !(isAudioVideoType(item) || isFolder(item))) return false;
            if (isLMS && !(isVideoType(item) || isFolder(item))) return false;
            if (isGallery && !(isImageType(item) || isVideoType(item) || isFolder(item))) return false;
            return true;
        });
    }

    function getFiles(folder = null, type = '') {

        // Reset selection state
        setActiveFile(null);
        if (!isBulkSelect && !ignoreResetSelections) {
            setActiveFiles([]);
        }

        setIsSearchResults(false);
        setIsLoading(type || true);

        // ---------- Resolve folder ----------
        let resolvedFolder = folder;

        if (initFolders) {

            if (!resolvedFolder) {
                setIsLoading(false);
                setActiveFolder(null);

                setAllFiles(prev => {
                    const updated = {...prev};

                    if (type === 'sort' && activeFolder && updated[activeFolder.id]) {
                        updated[activeFolder.id] = sortFiles(updated[activeFolder.id], sort);
                    } else {
                        updated[initParentFolder?.id || ''] = initFolders;
                    }

                    return updated;
                });

                return;
            }

            resolvedFolder = {
                ...resolvedFolder,
                ...(privateFolders && {isPrivate: true}),
                ...(isSearch && !initFolders.some(f => f.id === resolvedFolder.id) && {folders: initFolders}),
            };

        } else {

            if (!resolvedFolder && lastFolder?.accountId === activeAccount.id) {
                resolvedFolder = lastFolder;
            }

            if (!resolvedFolder) {
                resolvedFolder = getRootFolders(activeAccount.root_id, activeAccount);
            }
        }

        // ---------- Pagination ----------
        if (type !== 'lazy') {
            setPage(1);
        }


        // ---------- Request ----------
        wp.ajax
            .post('igd_get_files', {
                shortcodeId,
                data: {
                    folder: resolvedFolder,
                    sort,
                    refresh: type === 'refresh',
                    from_server: initFolders && initParentFolder?.id === resolvedFolder?.id,
                    fileNumbers,
                    limit: lazyLoad ? lazyLoadNumber : 0,
                    filters: getRequestFilters(),
                },
                nonce: nonce || igd.nonce,
            })
            .done((response) => {

                let {files: items = [], breadcrumbs, error} = response;

                if (error) {
                    if (type !== 'refresh') {
                        showSwalError(error);
                    }

                    return;
                }

                // ---------- Lazy append ----------
                if (
                    type === 'lazy' &&
                    lazyLoadType !== 'pagination' &&
                    activeFolder?.id === resolvedFolder.id
                ) {
                    items = [...files, ...items];
                }

                // ---------- Item filters ----------
                items = filterFiles(items);

                // ---------- Sort files ----------
                items = sortFiles(items, sort);
                // ---------- State updates ----------
                setAllFiles(prev => ({
                    ...prev,
                    [resolvedFolder.id]: items,
                    folders: [...(prev.folders || []), resolvedFolder],
                }));

                setActiveFolder(resolvedFolder);

                // ---------- Breadcrumbs ----------
                if (breadcrumbs) {
                    if (initFolders || isSpecificFolders) {

                        const selectedFolders = initFolders || activeAccount.specific_folders;

                        setBreadcrumbs(cleanupBreadcrumbs({
                            breadcrumbs,
                            folder: resolvedFolder,
                            initFolders: selectedFolders,
                            initParentFolder,
                            isSearch,
                            activeFolder,
                        }));
                    } else {
                        setBreadcrumbs({...breadcrumbs});
                    }
                }

            })
            .fail(error => {
                console.error(error);
                setPage(1);
                showSwalError(
                    typeof error === 'string'
                        ? error
                        : wp.i18n.__('Something went wrong! Please try again later.', 'integrate-google-drive')
                );
            })
            .always(() => setIsLoading(false));
    }

    function listFiles(folder) {

        const folderId = folder.id;

        setActiveFile(null);

        if (!isBulkSelect && !ignoreResetSelections) {
            setActiveFiles([]);
        }

        if (allFiles[folderId]) {

            folder = allFiles['folders'].find(item => item.id === folder.id) || folder;

            setActiveFolder(folder);

            function getBreadcrumbs(folderId) {

                if (isRootFolder(folderId, activeAccount)) {
                    const rootFolder = getRootFolders(folderId, activeAccount);
                    return {[folderId]: rootFolder.name};
                }

                const breadcrumbKeys = Object.keys(breadcrumbs);
                const folder = allFiles['folders'].find(item => item.id === folderId);

                if (folder) {
                    const items = {[folderId]: folder.name};
                    const folderParents = folder['parents'] || [];

                    // If is search, set breadcrumbs based on initial search results
                    if (isSearch && !isShortcodeBuilder && searchResults) {
                        if (folderParents.length && !(searchResults && searchResults.find(item => item.id === folderId))) {
                            return {...getBreadcrumbs(folderParents[0]), ...items};
                        }
                    } else {
                        if (folderParents.length && !(initFolders && initFolders.find(item => item.id === folderId))) {
                            return {...getBreadcrumbs(folderParents[0]), ...items};
                        }
                    }

                    return items;
                }

                if (breadcrumbKeys.includes(folderId)) {
                    const index = breadcrumbKeys.indexOf(folderId);
                    return Object.fromEntries(breadcrumbKeys.slice(0, index + 1).map(key => [key, breadcrumbs[key]]));
                }

                return {};
            }

            setBreadcrumbs({...getBreadcrumbs(folderId)});

        } else {
            getFiles(folder);
        }
    }

    function searchFiles(searchKeyword) {
        searchKeywordRef.current = searchKeyword;
        setIsSearchResults(true);
        setSearchResults(null);

        let folders = !isSearch && activeFolder ? [activeFolder] : initFolders.filter(item => isFolder(item));

        // Set active files to empty
        setActiveFile(null);

        if (!isBulkSelect && !ignoreResetSelections) {
            setActiveFiles([]);
        }

        // Set uploader false
        setIsUpload(false);

        let initFilesResults = [];

        if (initFolders) {
            // Search in initFolders by the keyword and merge with search results files
            initFilesResults = initFolders.filter(file => file.name.toLowerCase().includes(searchKeyword.toLowerCase()));

            // If no folders to search return init files results
            if (!folders.length) {
                setFiles(initFilesResults);
                setSearchResults(initFilesResults);
                return;
            }

        }

        setIsLoading(true);

        wp.ajax.post('igd_search_files', {
            shortcodeId,
            folders,
            sort,
            fileNumbers,
            keyword: searchKeyword,
            accountId: activeAccount['id'],
            isPrivate: privateFolders,
            fullTextSearch: permissions?.fullTextSearch,
            filters: getRequestFilters(),
            nonce: nonce || igd.nonce,
        }).done((files) => {

            if (initFilesResults.length) {
                files = [...initFilesResults, ...files];
            }

            // If not audio video item return if is media player
            if (isMedia) {
                files = files.filter(item => isAudioVideoType(item) || isFolder(item));
            }

            // If is LMS video selection
            if (isLMS) {
                files = files.filter(item => isVideoType(item) || isFolder(item));
            }

            if (isGallery) {
                files = files.filter(item => isImageType(item) || isVideoType(item) || isFolder(item));
            }

            // unique files
            const seen = new Set();
            files = files.filter(file => {
                if (seen.has(file.id)) {
                    return false;
                } else {
                    seen.add(file.id);
                    return true;
                }
            });

            //limit files number
            if (fileNumbers) {
                files = files.slice(0, fileNumbers);
            }

            setFiles(files);
            setSearchResults(files);

        }).fail(error => {
            console.log(error);

            Swal.fire({
                title: wp.i18n.__('Error!', 'integrate-google-drive'),
                text: typeof error === 'string' ? error : wp.i18n.__('Something went wrong! Please try again later.', 'integrate-google-drive'),
                icon: 'error',
                confirmButtonText: 'Ok',
                customClass: {container: 'igd-swal'},
            });

        }).always(() => {
            setIsLoading(false);
        });

        // Send search notification
        if (notifications && notifications.searchNotification) {
            wp.ajax.post('igd_notification', {
                files: folders,
                keyword: searchKeyword,
                notifications,
                type: 'search',
                nonce: nonce || igd.nonce,
            });
        }

    }

    // Handle Context Menu
    // Render a unique id for each context menu
    const contextMenuId = useMemo(() => `igd-context-menu-${Date.now()}`, []);
    const {show, hideAll} = useContextMenu({id: contextMenuId});

    const isMounted = useMounted();

    // Mount
    useEffect(() => {

        if (!Object.keys(accounts).length) return;

        // Initial search
        if (permissions && permissions.allowSearch && initialSearchTerm) {
            searchFiles(initialSearchTerm);
            return;
        }

        // Return if search module
        if (isSearch && !isShortcodeBuilder) return;

        // return init module
        if (initFolders || initParentFolder || 'specific_folders' === activeAccount.scope) {

            if (rememberLastFolder && lastFolder) {
                getFiles(lastFolder, 'last');
            }

            return; // always should return
        }

        // Load Files
        getFiles();

    }, []);

    // Handle module_refresh URL parameter - module_refresh files if URL parameter is present
    useEffect(() => {
        const urlParams = new URLSearchParams(window.location.search);
        const refresh = urlParams.get('module_refresh');

        if (refresh) {
            getFiles(activeFolder, 'module_refresh');
        }
    }, []);

    // Handle account switch
    useEffect(() => {
        if (!isMounted) return;

        igd.activeAccount = activeAccount;

        if (initFolders) {
            setShowSidebar(false);
            setActiveFolder(null);
        } else {
            setIsLoading('switch');
            setAllFiles({});

            if (!initFolders && !isShortcodeBuilder) {
                setShowSidebar(true);
            }
        }

        wp.ajax.post('igd_switch_account', {
            shortcodeId,
            id: activeAccount.id,
            nonce: nonce || igd.nonce,
        }).done(() => {

            if (initFolders) {
                if (initFolders.length === 1) {
                    listFiles(initFolders[0]);
                }

                return;
            }

            const rootFolder = {
                id: activeAccount.root_id,
                accountId: activeAccount.id,
                name: wp.i18n.__('My Drive', 'integrate-google-drive'),
                type: 'application/vnd.google-apps.folder',
            };

            if ('app_folder' === activeAccount.scope) {
                rootFolder.name = activeAccount.app_folder.name;
            }

            getFiles(rootFolder, 'switch');

        }).fail((error) => {
            console.log(error);
        });

    }, [activeAccount?.id]);

    // Check screen sizes
    useEffect(() => {
        const handleResize = () => {
            setIsMobile(window.innerWidth < 768);
        }

        window.addEventListener('resize', handleResize);

        return (() => {
            window.removeEventListener('resize', handleResize);
        })

    }, []);

    // Handle initParentFolder change
    useEffect(() => {
        if (!isMounted) return;

        if (!initParentFolder.id || initParentFolder.id === activeFolder?.id) return;

        // Load Files
        listFiles(initParentFolder);

    }, [initParentFolder]);

    // Handle all selections
    useEffect(() => {
        if (!isMounted) return;

        if (isShortcodeBuilder) {
            let newSelectedFiles;

            if (selectAll) {
                const allFiles = [...selectedFolders, ...files];
                const seen = new Set();

                newSelectedFiles = allFiles.filter(file => {

                    if (seen.has(file.id)) {
                        return false;
                    } else {
                        seen.add(file.id);
                        return true;
                    }
                });

            } else {
                newSelectedFiles = selectedFolders.filter(selectedFile =>
                    !files.some(file => file.id === selectedFile.id)
                );
            }

            setSelectedFolders(newSelectedFiles);

        } else if (!isGallery) {
            setActiveFiles(selectAll ? (permissions ? files.filter(file => !(isFolder(file) && !permissions.folderDownload)) : files) : []);
        }

    }, [selectAll]);

    // Handle bulk select
    useEffect(() => {
        if (!isMounted || isBulkSelect) return;

        setSelectAll(null);
        setActiveFiles([]);

    }, [isBulkSelect]);

    // Deselect all on folder change
    useEffect(() => {

        if (!isMounted) return;
        setPage(1);

        setSelectAll(false);

        if (!isBulkSelect && !ignoreResetSelections) {
            setActiveFiles([]);
        }

        if (activeFolder) {
            setIsSearchResults(false);
        }

    }, [activeFolder]);

    // Handle remember last folder session
    useEffect(() => {

        if (!isMounted) return;

        if (rememberLastFolder) {
            if (activeFolder) {
                setLastFolderSession(activeFolder, shortcodeId);
            } else {
                removeLastFolderSession(shortcodeId);
            }
        }

    }, [activeFolder]);

    // Hide react tooltip and context menu on scroll
    useEffect(() => {
        const hideTooltipMenu = () => {
            hideAll();
        }

        const element = document.querySelector('.igd-shortcode-builder-form');

        if (element) {
            element.addEventListener('scroll', hideTooltipMenu);
        }

        return () => {
            if (element) {
                element.removeEventListener('scroll', hideTooltipMenu);
            }
        }

    }, []);

    // Handle uploaded files
    useEffect(() => {

        const handleUploaded = ({detail}) => {
            const {folderId, files: newFiles} = detail;

            if (!activeFolder?.id || activeFolder.id !== folderId || !Array.isArray(newFiles)) return;

            const filterUnique = (existingFiles) => {
                const existingIds = new Set(existingFiles.map((f) => f.id));
                return newFiles.filter((f) => !existingIds.has(f.id));
            };

            setAllFiles((prev) => ({
                ...prev,
                [folderId]: [...(prev[folderId] || []), ...filterUnique(prev[folderId] || [])],
            }));

        };

        document.addEventListener('igd_upload_complete', handleUploaded);
        return () => document.removeEventListener('igd_upload_complete', handleUploaded);

    }, [activeFolder]);

    // Handle selection active files
    useEffect(() => {
        if (isShortcodeBuilder || (!isReview && !isGallery) || !selection) return;

        setActiveFiles(selection.files);
    }, []);

    // Handle page change - for lazy load pagination
    useEffect(() => {
        // Exit early for search result mode
        if (isSearchResults) {
            return;
        }

        // Clear files when searching without an active folder
        if (isSearch && !activeFolder) {
            setFiles([]);
            return;
        }

        const folderId = activeFolder?.id || '';
        const folderFiles = allFiles[folderId] || [];
        const fallbackFiles = allFiles[''] || initFolders || [];


        // No lazy load or search result inside a folder
        if (!lazyLoad || (isSearchResults && activeFolder) || !activeFolder) {
            setFiles(sortFiles(folderFiles, sort));

            return;
        }

        const isPagedLazyLoad = lazyLoadType === 'scroll' || lazyLoadType === 'button';

        const startIndex = isPagedLazyLoad
            ? 0
            : (page - 1) * lazyLoadNumber;

        const endIndex = isPagedLazyLoad
            ? page * lazyLoadNumber
            : startIndex + lazyLoadNumber;

        const sourceFiles = activeFolder ? folderFiles : fallbackFiles;
        let slicedFiles = sourceFiles.length > 0 ? sourceFiles.slice(startIndex, endIndex) : [];

        // Reset page if no files found on paginated lazy load
        if (!isPagedLazyLoad && !slicedFiles.length && page > 1) {
            setPage(1);
            return;
        }

        // Limit the files number if enabled
        if (fileNumbers) {
            slicedFiles = slicedFiles.slice(0, fileNumbers);
        }

        setFiles(sortFiles(slicedFiles, sort));

    }, [page, activeFolder, allFiles, initFolders, isSearchResults, searchResults, sort]);

    // Background fetch folders
    const fetchingFoldersRef = useRef(new Set());
    const isFetchingRef = useRef(false);

    useEffect(() => {
        const folders = files.filter(isFolder);

        const loadedFolderIds = new Set((allFiles.folders || []).map(f => f.id));

        const queue = folders.filter(folder => !loadedFolderIds.has(folder.id) &&
            !fetchingFoldersRef.current.has(folder.id));

        if (!queue.length || isFetchingRef.current) {
            return;
        }

        const fetchNextFolder = async () => {
            const folder = queue.shift();

            if (!folder) {
                isFetchingRef.current = false;
                return;
            }

            isFetchingRef.current = true;
            fetchingFoldersRef.current.add(folder.id);

            try {

                const {files: items = []} = await wp.ajax.post('igd_get_files', {
                    data: {
                        folder,
                        sort,
                        filters: getRequestFilters(),
                        skipBreadcrumbs: true,
                    },
                    nonce: nonce || igd.nonce,
                });

                setAllFiles(prev => ({
                    ...prev,
                    [folder.id]: items,
                    folders: [...(prev.folders || []), folder],
                }));

            } catch (error) {
                // Allow retry on failure
                fetchingFoldersRef.current.delete(folder.id);
            } finally {

                // Small delay BEFORE loading the next folder
                await new Promise(r => setTimeout(r, 150));

                // Continue with next folder
                fetchNextFolder();
            }
        }

        fetchNextFolder();

    }, [files]);

    return (
        !!Object.keys(accounts).length ?

            <AppProvider
                value={{
                    accounts,
                    shortcodeId,
                    setActiveAccount,
                    activeAccount,
                    activeFiles,
                    setActiveFiles,
                    activeFile,
                    setActiveFile,
                    activeFolder,
                    setActiveFolder,
                    permissions,
                    notifications,
                    shortcodeBuilderType,
                    isList, setIsList,
                    showDetails, setShowDetails,
                    setIsUpload,
                    getFiles,
                    isShortcodeBuilder,
                    isMobile,
                    allFolders,
                    initFolders,
                    isSearch,
                    setShowSidebar,
                    sort,
                    setSort,
                    setAllFiles,
                    breadcrumbs,
                    setBreadcrumbs,
                    isSearchResults,
                    setIsSearchResults,
                    searchResults,
                    setSearchResults,
                    isOptions,
                    setIsOptions,
                    listFiles,
                    showBreadcrumbs,
                    showRefresh,
                    showSorting,
                    showFilesCount,
                    initParentFolder,
                    isUpload,
                    isLoading,
                    files,
                    allFiles,
                    selectedFolders,
                    setSelectedFolders,
                    filters,
                    showLastModified,
                    showFileSizeField,
                    searchBoxText,

                    galleryLayout,
                    galleryAspectRatio,
                    galleryColumns,
                    galleryHeight,
                    galleryMargin,
                    galleryView,
                    galleryFolderView,
                    thumbnailCaption,
                    galleryOverlay,
                    overlayDisplayType,
                    galleryOverlayTitle,
                    galleryOverlayDescription,
                    galleryOverlaySize,
                    galleryImageSize,
                    galleryCustomSizeWidth,
                    galleryCustomSizeHeight,

                    show,
                    hideAll,
                    contextMenuId,
                    isSelectFiles,
                    selectionType,
                    isBulkSelect, setIsBulkSelect,
                    selectAll, setSelectAll,
                    searchFiles,
                    initialSearchTerm,

                    searchKeywordRef,
                    searchKeyword,
                    setSearchKeyword,
                    isLMS,
                    isWooCommerce,
                    lazyLoad,
                    lazyLoadNumber,
                    lazyLoadType,
                    nonce,

                    selection,
                    isFormUploader,
                    showHeader,
                    uploadFileName,
                    page,
                    setPage,
                    overwrite,
                }}
            >
                <div className="igd-file-browser">

                    {/*------- Context Menu ------*/}
                    <ContextMenu/>

                    {/*----------- Header ------------*/}
                    {showHeader && (!isSearch || isSearchResults || activeFolder) && <Header/>}

                    <div className="igd-file-browser-body">

                        {/*----------- Sidebar ------------*/}
                        {showSidebar && <Sidebar/>}

                        <Body/>

                        {/*------ Details -----*/}
                        {!isShortcodeBuilder && showDetails && <Details/>}

                    </div>
                </div>
            </AppProvider>

            :

            (!initFolders ? <AddAccountPlaceholder/> : null)
    )
}