import "../../../../assets/css/advancedAppLauncher.css";
// Import js modules
import React, { useEffect, useState } from "react";
// Import React components
import Content from "./components/Content";
import AddNewAppForm from "./components/AddNewAppForm";
import FoldersList from "./components/FoldersList";
import LeftNavBottomLinks from "./components/LeftNavBottomLinks";
import { FEAGlobals } from "../../../../../common/Globals";
import { convertDistributedStoreToPrefs, initializeAdvancedAppLauncherPrefs } from "./initializeStore";
import { NEW_FOLDER_NAME } from "./constants";
import { debounce } from "lodash";
let logger;
/**
 * The "app" button that launches the Advanced App Launcher.
 * The Advanced App Launcher comes with search capabilities and allows users to organize their application components into folders.
 *
 * @param {boolean} [enableQuickComponents=false] DEPRECATED: Set to true to allow users to create their own components
 */
export const AdvancedAppLauncher = (props) => {
    var _a;
    const [isFormVisible, setIsFormVisible] = useState(false);
    const [foldersList, setFoldersList] = useState([]);
    const [folders, setFolders] = useState({});
    const [activeFolder, setActiveFolder] = useState("");
    const [dragDisabled, setDragDisabled] = useState([]);
    const [sortBy, setSortBy] = useState("Alphabetical");
    const [allApps, setAllApps] = useState([]);
    const [activeTags, setActiveTags] = useState([]);
    // Converts what we know about an iocd application (name and maybe url) into an IconDescriptor which can be used for rendering
    const makeIcon = (name, iconUrl) => iconUrl
        ? {
            imageType: "url",
            path: iconUrl,
            category: "application",
        }
        : {
            imageType: "initials",
            name,
            category: "application",
        };
    const setDefaultPrefs = async (myGlue) => {
        const { data } = await FEAGlobals.FSBL.Clients.ConfigClient.get([
            "finsemble",
            "servicesConfig",
            "distributedStore",
            "initialStores",
        ]);
        const config = (data !== null && data !== void 0 ? data : []).find((c) => c.name === "Finsemble-AppLauncher-Store");
        myGlue.prefs.setFor("IO.Global.Launcher", {
            ...(config ? convertDistributedStoreToPrefs(config) : initializeAdvancedAppLauncherPrefs()),
        });
    };
    const initialization = async () => {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j;
        // Filters out hidden apps (those that shouldn't display in launcher) and sets an app as deletable if isUserDefined
        // is set as a custom property
        const filterApps = (applications) => applications
            .filter((app) => !app.hidden)
            .map((app) => {
            var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
            return {
                appID: app.name,
                tags: (_a = app.keywords) !== null && _a !== void 0 ? _a : [],
                name: app.name,
                icon: makeIcon(app.name, app.iconURL),
                url: (_j = (_e = (_d = (_c = (_b = app.userProperties) === null || _b === void 0 ? void 0 : _b.appd) === null || _c === void 0 ? void 0 : _c.details) === null || _d === void 0 ? void 0 : _d.url) !== null && _e !== void 0 ? _e : (_h = (_g = (_f = app.customProperties) === null || _f === void 0 ? void 0 : _f.appd) === null || _g === void 0 ? void 0 : _g.details) === null || _h === void 0 ? void 0 : _h.url) !== null && _j !== void 0 ? _j : "",
                type: (_q = (_m = (_l = (_k = app.userProperties) === null || _k === void 0 ? void 0 : _k.appd) === null || _l === void 0 ? void 0 : _l.type) !== null && _m !== void 0 ? _m : (_p = (_o = app.customProperties) === null || _o === void 0 ? void 0 : _o.appd) === null || _p === void 0 ? void 0 : _p.type) !== null && _q !== void 0 ? _q : "",
                canDelete: ((_r = app.userProperties) === null || _r === void 0 ? void 0 : _r["isUserDefined"]) || ((_s = app.customProperties) === null || _s === void 0 ? void 0 : _s["isUserDefined"]),
                displayName: app.title,
            };
        });
        if (!logger) {
            logger = globalThis.iop.logger.subLogger("Toolbar");
            logger.publishLevel("debug");
        }
        const debouncedAppUpdate = debounce(async () => {
            const configs = await globalThis.iop.appManager.getConfigurations();
            logger.debug(`AdvancedAppLauncher.debouncedAppUpdate - ${configs.length} apps are in config`);
            const filteredApps = filterApps(configs);
            logger.debug(`AdvancedAppLauncher.debouncedAppUpdate - Updating menu with ${filteredApps.length} apps after filtering`);
            setAllApps(filteredApps);
        }, 1000);
        debouncedAppUpdate();
        // The subscription immediately fires for every single app that has already been added. Let's filter out the ones we're already aware of.
        globalThis.iop.appManager.onAppAdded(debouncedAppUpdate);
        globalThis.iop.appManager.onAppChanged(debouncedAppUpdate);
        globalThis.iop.appManager.onAppRemoved(debouncedAppUpdate);
        const prefs = await globalThis.iop.prefs.get("IO.Global.Launcher");
        globalThis.iop.prefs.subscribeFor("IO.Global.Launcher", (prefsUpdate) => {
            var _a, _b, _c, _d, _e, _f;
            console.debug("Advanced App Launcher preferences update: ", prefsUpdate);
            logger.debug(`Advanced App Launcher preferences update: ${JSON.stringify(prefsUpdate.data, null, "\t")}`);
            setActiveFolder((_a = prefsUpdate.data.activeFolder) !== null && _a !== void 0 ? _a : "");
            setActiveTags((_b = prefsUpdate.data.activeTags) !== null && _b !== void 0 ? _b : []);
            setDragDisabled((_c = prefsUpdate.data.dragDisabled) !== null && _c !== void 0 ? _c : []);
            setFolders((_d = prefsUpdate.data.folders) !== null && _d !== void 0 ? _d : {});
            setFoldersList((_e = prefsUpdate.data.foldersList) !== null && _e !== void 0 ? _e : []);
            setSortBy((_f = prefsUpdate.data.sortBy) !== null && _f !== void 0 ? _f : "Alphabetical");
        });
        // Initialize state from existing prefs
        if ((prefs === null || prefs === void 0 ? void 0 : prefs.data) || prefs) {
            const prefsData = (_a = prefs === null || prefs === void 0 ? void 0 : prefs.data) !== null && _a !== void 0 ? _a : prefs;
            setActiveFolder((_b = prefsData.activeFolder) !== null && _b !== void 0 ? _b : "");
            setActiveTags((_c = prefsData.activeTags) !== null && _c !== void 0 ? _c : []);
            setDragDisabled((_d = prefsData.dragDisabled) !== null && _d !== void 0 ? _d : []);
            setFolders((_e = prefsData.folders) !== null && _e !== void 0 ? _e : {});
            setFoldersList((_f = prefsData.foldersList) !== null && _f !== void 0 ? _f : []);
            setSortBy((_g = prefsData.sortBy) !== null && _g !== void 0 ? _g : "Alphabetical");
        }
        // if there aren't any prefs yet then this is first time usage. We initialize the prefs from
        // the initialStores section of config
        if (Object.keys((_j = (_h = prefs === null || prefs === void 0 ? void 0 : prefs.data) !== null && _h !== void 0 ? _h : prefs) !== null && _j !== void 0 ? _j : {}).length === 0) {
            await setDefaultPrefs(globalThis.iop);
        }
    };
    const updateActiveFolder = (newActiveFolder) => {
        var _a;
        (_a = globalThis.iop) === null || _a === void 0 ? void 0 : _a.prefs.updateFor("IO.Global.Launcher", {
            activeFolder: newActiveFolder,
        });
    };
    const renameFolder = (oldName, newName) => {
        var _a;
        // Create a new state for all the folders/metadata
        const newFolders = {
            // Get all of the previous folders
            ...folders,
            // Create a duplicate of the folder under the new name
            [newName]: {
                // Copy all the metadata
                ...folders[oldName],
                // Use the new name for the folder
                name: newName,
            },
        };
        // Delete the old folder
        delete newFolders[oldName];
        // Create a new version of the state for the folders list
        const newFoldersList = foldersList.slice(0);
        // In the folders list, replace the old name with the new name
        newFoldersList.splice(newFoldersList.indexOf(oldName), 1, newName);
        // Update the app preferences with the new state info
        (_a = globalThis.iop) === null || _a === void 0 ? void 0 : _a.prefs.updateFor("IO.Global.Launcher", {
            folders: newFolders,
            foldersList: newFoldersList,
        });
    };
    const reorderFolder = (oldIndex, newIndex) => {
        var _a;
        const newFolderList = foldersList.slice(0);
        // Which folder is moving?
        const movingFolderName = foldersList[dragDisabled.length + oldIndex];
        // Remove folder from old location
        newFolderList.splice(dragDisabled.length + oldIndex, 1);
        // Add folder name to new location
        newFolderList.splice(dragDisabled.length + newIndex, 0, movingFolderName);
        // Save new order
        (_a = globalThis.iop) === null || _a === void 0 ? void 0 : _a.prefs.updateFor("IO.Global.Launcher", {
            foldersList: newFolderList,
        });
    };
    const addAppToFolder = (folderName, appd) => {
        var _a;
        const newFolders = folders;
        newFolders[folderName].apps.push(appd);
        (_a = globalThis.iop) === null || _a === void 0 ? void 0 : _a.prefs.updateFor("IO.Global.Launcher", {
            folders: newFolders,
        });
    };
    const removeAppFromFolder = (folderName, appName) => {
        // We'll need to update app preferences with the new folder structure, where FOLDERNAME doesn't include APPNAME in its app list
        var _a;
        // Create a new variable to mess with the preferences
        const newFolders = folders;
        // To prevent runtime errors, confirm that the provided folderName is available
        if (!(folderName in newFolders)) {
            console.warn(`Can't remove app from unknown folder "${folderName}". Available folder names: ${Object.keys(newFolders).join(", ")}.`);
            return;
        }
        // Find the index of the app we want to remove
        const appIndex = newFolders[folderName].apps.findIndex((app) => app.name === appName);
        // To prevent deleting something unexpected, confirm that the app is present in the list
        if (appIndex === -1) {
            console.warn(`Can't find appName "${appName}" in folder "${folderName}. Available app names: ${newFolders[folderName].apps
                .map(({ name }) => name)
                .join(", ")}`);
            return;
        }
        // Remove the app from the list
        newFolders[folderName].apps.splice(appIndex, 1);
        // Update app preferences
        (_a = globalThis.iop) === null || _a === void 0 ? void 0 : _a.prefs.updateFor("IO.Global.Launcher", {
            folders: newFolders,
        });
    };
    const addFolder = () => {
        var _a;
        // Create a folder name
        let newName = `${NEW_FOLDER_NAME}`;
        let i = 1;
        // If there's already a folder named "New Folder", try the name "New Folder 1", "New Folder 2", etc, until you find a name that isn't taken yet.
        while (newName in folders) {
            newName = `${NEW_FOLDER_NAME} ${i}`;
            i++;
        }
        // Update folder metadata and list of folder names
        (_a = globalThis.iop) === null || _a === void 0 ? void 0 : _a.prefs.updateFor("IO.Global.Launcher", {
            folders: {
                ...folders,
                [newName]: {
                    name: newName,
                    disableUserRemove: true,
                    icon: "ff-adp-hamburger",
                    canEdit: true,
                    canDelete: true,
                    apps: [],
                },
            },
            foldersList: [...foldersList, newName],
        });
    };
    const deleteFolder = (folderName) => {
        var _a;
        // Delete the folder from the metadata of all folders
        const newFolders = folders;
        delete newFolders[folderName];
        // Delete the folder name from the list of names
        const newFoldersList = foldersList.slice(0);
        newFoldersList.splice(newFoldersList.indexOf(folderName), 1);
        // Update internal state
        (_a = globalThis.iop) === null || _a === void 0 ? void 0 : _a.prefs.updateFor("IO.Global.Launcher", {
            folders: newFolders,
            foldersList: newFoldersList,
        });
    };
    const deleteTag = (tag) => {
        var _a;
        const tagList = activeTags.slice(0);
        const index = tagList.indexOf(tag);
        if (index === -1) {
            console.warn(`Can't find delete unknown tag "${tag}".`);
            return;
        }
        tagList.splice(index, 1);
        (_a = globalThis.iop) === null || _a === void 0 ? void 0 : _a.prefs.updateFor("IO.Global.Launcher", {
            activeTags: tagList,
            tags: tagList, // Backward compatibility with Finsemble v7
        });
    };
    const addTag = (tag) => {
        var _a;
        const tagList = activeTags.slice(0);
        tagList.push(tag);
        (_a = globalThis.iop) === null || _a === void 0 ? void 0 : _a.prefs.updateFor("IO.Global.Launcher", {
            activeTags: tagList,
            tags: tagList, // Backward compatibility with Finsemble v7
        });
    };
    const addApp = (appd, cb) => {
        FEAGlobals.FSBL.Clients.AppsClient.addUserDefinedComponent(appd).then(({ err }) => {
            cb === null || cb === void 0 ? void 0 : cb(err);
        });
    };
    const deleteApp = (name) => {
        FEAGlobals.FSBL.Clients.AppsClient.removeUserDefinedComponent({ name });
    };
    const clickSortBy = (newSortBy) => {
        var _a;
        (_a = globalThis.iop) === null || _a === void 0 ? void 0 : _a.prefs.updateFor("IO.Global.Launcher", { sortBy: newSortBy });
    };
    const handleWindowBlurred = () => {
        setIsFormVisible(false);
    };
    useEffect(() => {
        initialization();
        window.addEventListener("blur", handleWindowBlurred);
        return () => {
            window.removeEventListener("blur", handleWindowBlurred);
        };
    }, []);
    if (!globalThis.iop)
        return null;
    return (React.createElement("div", { className: "advanced-app-launcher", style: { minHeight: "400px" } },
        React.createElement("div", { className: "complex-menu-wrapper" },
            React.createElement("div", { className: "complex-menu-left-nav" },
                React.createElement(FoldersList, { folders: folders, foldersList: foldersList, activeFolder: activeFolder, updateActiveFolder: updateActiveFolder, dragDisabled: dragDisabled, renameFolder: renameFolder, reorderFolder: reorderFolder, addAppToFolder: addAppToFolder, deleteFolder: deleteFolder }),
                React.createElement(LeftNavBottomLinks, { enableQuickComponents: props.enableQuickComponents, addNewAppFolder: addFolder, showAddAppForm: () => setIsFormVisible(true), removeStore: async () => {
                        var _a;
                        await ((_a = globalThis.iop) === null || _a === void 0 ? void 0 : _a.prefs.clearFor("IO.Global.Launcher"));
                        await setDefaultPrefs(globalThis.iop);
                    } })),
            React.createElement(Content, { folder: (_a = folders[activeFolder]) !== null && _a !== void 0 ? _a : {}, sortBy: sortBy, setSortBy: clickSortBy, allApps: allApps, activeTags: activeTags, deleteTag: deleteTag, addTag: addTag, deleteApp: deleteApp, removeApp: removeAppFromFolder, addAppToFolder: addAppToFolder, removeAppFromFolder: removeAppFromFolder }),
            isFormVisible && (React.createElement(AddNewAppForm, { activeFolder: activeFolder, addApp: addApp, onDone: () => setIsFormVisible(false), customURLValidation: props.customURLValidation, customAddComponent: props.customAddComponent, beforeAddComponent: props.beforeAddComponent })))));
};
//# sourceMappingURL=AdvancedAppLauncher.js.map