/*!
 * Copyright 2017 - 2020 by ChartIQ, Inc.
 * All rights reserved.
 */
import AppDirectory from "../modules/AppDirectory";
import FDC3 from "../modules/FDC3";
import { getStore } from "./appStore";
import { findIndex } from "lodash";
export const findAppIndexInFolder = (appID, folderName, folders) => findIndex(folders[folderName].apps, (app) => app.appID === appID);
const data = {};
let FDC3Client;
let appd;
function initialize(done = Function.prototype) {
    FSBL.Clients.ConfigClient.get(["finsemble", "appDirectoryEndpoint"], (err, appDirectoryEndpoint) => {
        FDC3Client = new FDC3({ url: appDirectoryEndpoint });
        appd = new AppDirectory(FDC3Client);
        const store = getStore();
        data.apps = store.values.apps;
        store.get(["appFolders", "folders"], (_err, folders) => {
            data.folders = folders;
            store.addListener({ field: "appFolders.folders" }, (__err, dt) => (data.folders = dt.value));
        });
        store.get(["activeFolder"], (_err, active) => {
            data.activeFolder = active;
            store.addListener({ field: "activeFolder" }, (__err, dt) => (data.activeFolder = dt.value));
        });
        store.get(["defaultFolder"], (_err, folder) => {
            data.defaultFolder = folder;
        });
        data.installed = store.values.appDefinitions;
        data.tags = store.values.tags;
        data.filteredApps = store.values.filteredApps;
        data.activeTags = store.values.activeTags;
        data.activeApp = store.values.activeApp;
        data.searchText = store.values.searchText;
        data.forceSearch = store.values.forceSearch;
        data.ADVANCED_APP_LAUNCHER = store.values.defaultFolder;
        store.addListener({ field: "apps" }, (_err, dt) => (data.apps = dt.value));
        store.addListener({ field: "appDefinitions" }, (_err, dt) => (data.installed = dt.value));
        store.addListener({ field: "tags" }, (_err, dt) => (data.tags = dt.value));
        store.addListener({ field: "activeApp" }, (_err, dt) => (data.activeApp = dt.value));
        store.addListener({ field: "activeTags" }, (_err, dt) => (data.activeTags = dt.value));
        store.addListener({ field: "filteredApps" }, (_err, dt) => (data.filteredApps = dt.value));
        store.addListener({ field: "searchText" }, (_err, dt) => (data.searchText = dt.value));
        store.addListener({ field: "forceSearch" }, (_err, dt) => (data.forceSearch = dt.value));
        done();
    });
}
/**
 * Clears all active tags
 */
function _clearActiveTags() {
    getStore().setValue({ field: "activeTags", value: [] });
}
/**
 * Async function to fetch apps from the FDC3 api (appD)
 */
async function getApps() {
    return await appd.getAll((err, retrievedApps) => {
        if (!err) {
            getStore().setValue({
                field: "apps",
                value: retrievedApps,
            });
        }
    });
}
/**
 * Call to appD to get the list of all tags
 */
async function getTags() {
    return await appd.getTags((err, retrievedTags) => {
        getStore().setValue({
            field: "tags",
            value: retrievedTags,
        });
    });
}
/**
 * Function to write errors to the log
 * @param {string} message The log message
 * @param {string} [protocol] Provide the logging protocol (default is error)
 */
function writeToLog(message, protocol = "error") {
    FSBL.Clients.Logger[protocol](message);
}
/**
 * Function to "install" an app. Adds the id to a list of installed apps
 * @param {string} name The name of the app
 */
async function addApp(id, cb = Function.prototype) {
    const { activeApp, installed, apps } = data;
    const appID = id;
    const app = apps.find((checkedApp) => checkedApp.appId === appID);
    if (!app) {
        FSBL.Clients.Logger.system.error(`Attempted to install app with id: ${id}, but could not find it in the local store.`);
        return cb();
    }
    const name = app.title || app.name;
    const folder = data.activeFolder;
    if (app === undefined) {
        console.warn("App not found.");
        return cb();
    }
    let manifest;
    let appConfig;
    // Manifest from FDC3 is a string property which can either be a stringified JSON, or a uri which delivers valid JSON.
    // The catalog will attempt to parse the string as JSON, then fetch from a URL if that fails.
    // If both paths fail, notify the user that this app can't be added
    if (app.manifestType.toLowerCase() === "finsemble") {
        try {
            manifest = JSON.parse(app.manifest);
        }
        catch (e) {
            try {
                const urlRes = await fetch(app.manifest, { method: "GET" });
                manifest = await urlRes.json();
            }
            catch (err) {
                writeToLog(`${name} is missing a valid manifest or URI that delivers a valid JSON manifest. Unable to add app.`, "error");
                return cb();
            }
        }
        finally {
            appConfig = installed[appID] = {
                appID,
                tags: app.tags,
                name: name.trim(),
                type: "component",
                manifest,
                canDelete: true,
                source: "FDC3",
            };
        }
    }
    else {
        writeToLog(`${name} does not appear to be a Finsemble manifest. This app cannot be added to Finsemble.`, "error");
        return cb();
    }
    const ADVANCED_APP_LAUNCHER = data.defaultFolder;
    const { folders } = data;
    folders[ADVANCED_APP_LAUNCHER].apps.push(appConfig);
    if (folder !== ADVANCED_APP_LAUNCHER)
        folders[folder].apps.push(appConfig);
    FSBL.Clients.LauncherClient.registerComponent({
        componentType: appConfig.name,
        manifest: appConfig.manifest,
    }, () => {
        getStore().setValues([
            {
                field: "activeApp",
                value: activeApp,
            },
            {
                field: "appDefinitions",
                value: installed,
            },
            {
                field: "appFolders.folders",
                value: folders,
            },
        ], cb);
    });
}
/**
 * Function to "uninstall" an app. Removes the id from a list of installed apps
 * @param {string} name The name of the app
 */
function removeApp(id, cb = Function.prototype) {
    let { installed, folders } = data;
    FSBL.Clients.LauncherClient.unRegisterComponent({ componentType: installed[id].name }, (err) => {
        if (err) {
            console.warn("Failed to deregister a component");
            return;
        }
        for (const key in data.folders) {
            const appIndex = findAppIndexInFolder(id, key, data.folders);
            if (appIndex > -1) {
                folders[key].apps.splice(appIndex, 1);
            }
        }
        // Delete the app from the list
        delete installed[id];
        getStore().setValues([
            {
                field: "appDefinitions",
                value: installed,
            },
            {
                field: "appFolders.folders",
                value: folders,
            },
        ], cb);
    });
}
/**
 * Function to set the 'active app' for the catalog.
 * @param {string} id The app id to show as the actively showcasing app
 */
function openApp(id) {
    let { apps } = data;
    let index = apps.findIndex((app) => app.appId === id);
    if (index > -1) {
        getStore().setValue({
            field: "activeApp",
            value: id,
        });
    }
}
/**
 * Clear the activeApp in store
 */
function clearApp() {
    getStore().setValue({
        field: "activeApp",
        value: null,
    });
}
/**
 * Return activeApp from store
 *
 * @returns {string} activeApp
 */
function getActiveApp() {
    return data.activeApp;
}
/**
 * Set the activeApp param in store
 *
 * @param {*} app
 */
function setActiveApp(app) {
    getStore().setValue({
        field: "activeApp",
        value: app,
    });
}
/**
 * Retrieves a list of installed apps by id
 */
function getInstalledApps() {
    return data.installed;
}
/**
 * Gets the list of filtered apps (when searching/filtering by tags)
 */
function getFilteredApps() {
    return data.filteredApps;
}
/**
 * Clears the list of filtered apps
 */
function clearFilteredApps() {
    getStore().setValue({
        field: "filteredApps",
        value: [],
    });
}
/**
 * Set the value of the search text in store
 *
 * @param {set} val Search string
 */
function setSearchValue(val) {
    getStore().setValue({ field: "searchText", value: val });
}
/**
 * Get the current value of the text in the store
 *
 * @returns {string}  Search string
 */
function getSearchValue() {
    return data.searchText;
}
/**
 * Clears the search text in store
 *
 */
function clearSearchText() {
    getStore().setValue({ field: "searchText", value: "" });
}
/**
 * Get forceSearch store value
 *
 * @returns {boolean} forceSearch
 */
function getForceSearch() {
    return data.forceSearch;
}
/**
 * Set the forceSearch value
 *
 * @param {string} val Boolean value for forceSearch
 */
function setForceSearch(val) {
    getStore().setValue({ field: "forceSearch", value: val });
}
/**
 * Gets the list of active tags (these are tags that are actively filtering the content list)
 */
function getActiveTags() {
    return data.activeTags; // type: string[]
}
/**
 * Removes all tags from the active tags list
 */
function clearTags() {
    _clearActiveTags();
}
/**
 * Return to the App Catalog home page
 */
function goHome() {
    setForceSearch(false);
    clearFilteredApps();
    clearApp();
    clearTags();
    clearSearchText();
}
/**
 * Send the search text and tags to the appd server and get a list of apps
 */
function filterApps() {
    let { activeTags, searchText } = data;
    if (activeTags.length === 0 && searchText === "") {
        goHome();
    }
    else {
        appd.search({ text: searchText, tags: activeTags }, (err, retrievedData) => {
            if (err) {
                FSBL.Logger.system.error(`FDC3 App search failed!: ${err}`);
                return;
            }
            getStore().setValue({ field: "activeApp", value: null });
            getStore().setValue({ field: "activeTags", value: activeTags });
            getStore().setValue({ field: "filteredApps", value: retrievedData });
        });
    }
}
/**
 * Refreshes the active tags search
 */
function refreshTagSearch() {
    filterApps();
}
/**
 * Calls appD to search the directory of apps based on search text and tag names
 * @param {string} terms The search terms provided by the user
 */
function searchApps(terms, cb = Function.prototype) {
    data.searchText = terms;
    setForceSearch(true);
    getStore().setValue({
        field: "searchText",
        value: terms,
    }, () => {
        filterApps();
        cb();
    });
}
/**
 * Private function to remove an active tag. This will filter apps based on tags
 * @param {string} tag The name of the tag
 */
function _removeActiveTag(tag) {
    data.activeTags = data.activeTags.filter((currentTag) => currentTag !== tag);
    getStore().setValue({ field: "activeTags", value: data.activeTags });
    filterApps();
}
/**
 * Removes an 'active tag' from the list of filtered tags
 * @param {string} tag The tag name
 */
function removeTag(tag) {
    _removeActiveTag(tag);
}
/**
 * Private function to add an active tag. This will filter apps based on tags
 * @param {string} tag The name of the tag
 */
function _addActiveTag(tag) {
    let { activeTags } = data;
    if (!activeTags.includes(tag)) {
        activeTags.push(tag);
    }
    getStore().setValue({ field: "activeTags", value: activeTags }, () => {
        filterApps();
    });
}
/**
 * Adds an 'active tag' to the list of filtered tags
 * @param {string} tag The tag name
 */
function addTag(tag) {
    _addActiveTag(tag);
}
export default {
    initialize,
    getApps,
    getFilteredApps,
    clearFilteredApps,
    searchApps,
    getActiveTags,
    getTags,
    addTag,
    removeTag,
    refreshTagSearch,
    clearTags,
    clearSearchText,
    addApp,
    removeApp,
    openApp,
    clearApp,
    getInstalledApps,
    getActiveApp,
    setActiveApp,
    filterApps,
    setSearchValue,
    setForceSearch,
    getSearchValue,
    getForceSearch,
    goHome,
};
//# sourceMappingURL=storeActions.js.map