/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

const fs = require('fs-extra');
const fspath = require("path");

const log = require("@node-red/util").log;
const util = require("./util");

const configSections = ['nodes','users','projects','modules'];

const settingsCache = {};

var globalSettingsFile;
var globalSettingsBackup;
var settings;

async function migrateToMultipleConfigFiles() {
    const nodesFilename = getSettingsFilename("nodes");
    if (fs.existsSync(nodesFilename)) {
        // We have both .config.json and .config.nodes.json
        // Use the more recently modified. This handles users going back to pre1.2
        // and up again.
        // We can remove this logic in 1.3+ and remove the old .config.json file entirely
        //
        const fsStatNodes = await fs.stat(nodesFilename);
        const fsStatGlobal = await fs.stat(globalSettingsFile);
        if (fsStatNodes.mtimeMs > fsStatGlobal.mtimeMs) {
            // .config.nodes.json is newer than .config.json - no migration needed
            return;
        }
    }
    const data = await util.readFile(globalSettingsFile,globalSettingsBackup,{});
    // In a later release we should remove the old settings file. But don't do
    // that *yet* otherwise users won't be able to downgrade easily.
    return writeSettings(data) // .then( () => fs.remove(globalSettingsFile) );
}


/**
 * Takes the single settings object and splits it into separate files. This makes
 * it easier to backup selected parts of the settings and also helps reduce the blast
 * radius if a file is lost.
 *
 * The settings are written to four files:
 *  - .config.nodes.json - the node registry
 *  - .config.users.json - user specific settings (eg editor settings)
 *  - .config.projects.json - project settings, including the active project
 *  - .config.modules.json - external modules installed by the runtime
 *  - .config.runtime.json - everything else - most notable _credentialSecret
 */
function writeSettings(data) {
    const configKeys = Object.keys(data);
    const writePromises = [];
    configSections.forEach(key => {
        const sectionData = data[key] || {};
        delete data[key];
        const sectionFilename = getSettingsFilename(key);
        const sectionContent = JSON.stringify(sectionData,null,4);
        if (sectionContent !== settingsCache[key]) {
            settingsCache[key] = sectionContent;
            writePromises.push(util.writeFile(sectionFilename,sectionContent,sectionFilename+".backup"))
        }
    })
    // Having extracted nodes/users/projects, write whatever is left to the runtime config
    const sectionFilename = getSettingsFilename("runtime");
    const sectionContent = JSON.stringify(data,null,4);
    if (sectionContent !== settingsCache["runtime"]) {
        settingsCache["runtime"] = sectionContent;
        writePromises.push(util.writeFile(sectionFilename,sectionContent,sectionFilename+".backup"));
    }
    return Promise.all(writePromises);
}

async function readSettings() {
    // Read the 'runtime' settings file first
    const runtimeFilename = getSettingsFilename("runtime");
    const result = await util.readFile(runtimeFilename,runtimeFilename+".backup",{});
    settingsCache["runtime"] = JSON.stringify(result, null ,4);
    const readPromises = [];
    // Read the other settings files and add them into the runtime settings
    configSections.forEach(key => {
        const sectionFilename = getSettingsFilename(key);
        readPromises.push(util.readFile(sectionFilename,sectionFilename+".backup",{}).then(sectionData => {
            settingsCache[key] = JSON.stringify(sectionData, null ,4);
            if (Object.keys(sectionData).length > 0) {
                result[key] = sectionData;
            }
        }))
    });
    return Promise.all(readPromises).then(() => result);
}

function getSettingsFilename(section) {
    return fspath.join(settings.userDir,`.config.${section}.json`);
}

module.exports = {
    init: function(_settings) {
        settings = _settings;
        globalSettingsFile = fspath.join(settings.userDir,".config.json");
        globalSettingsBackup = fspath.join(settings.userDir,".config.json.backup");

        if (fs.existsSync(globalSettingsFile) && !settings.readOnly) {
            return migrateToMultipleConfigFiles();
        } else {
            return Promise.resolve();
        }
    },
    getSettings: function() {
        return readSettings()
    },
    saveSettings: function(newSettings) {
        if (settings.readOnly) {
            return Promise.resolve();
        }
        return writeSettings(newSettings);
    }
}
