// Copyright (C) 2023-2025 IITC-CE - GPL-3.0 with Store Exception - see LICENSE and COPYING.STORE

import { isSet, parseMeta, sanitizeFileName } from './helpers.js';
import deepmerge from '@bundled-es-modules/deepmerge';

/**
 * Processes the input parameters for backup data retrieval.
 *
 * This function takes an input object containing parameters for backup data retrieval
 * and returns a new object with processed parameters. If the input parameters are not
 * an object, an empty object is used as the default value. The function combines the
 * input parameters with default parameters to ensure all required properties are present.
 *
 * @param {BackupParams} params - The parameters for setting the backup data.
 * @returns {Object} The processed parameters object.
 */
export function paramsProcessing(params) {
    if (typeof params !== 'object') params = {};

    // Default parameters
    const default_params = {
        settings: false,
        data: false,
        external: false,
    };

    // Combine the default parameters with the input parameters using spread syntax
    return { ...default_params, ...params };
}

/**
 * Exports specific IITC settings from the provided storage object.
 *
 * This function takes a storage object and extracts specific IITC settings based on
 * predefined keys. It creates a new object containing only the specified IITC settings
 * and returns it.
 *
 * @param {Object} all_storage - The storage object containing all data.
 * @returns {Object} An object containing specific IITC settings.
 */
export const exportIitcSettings = (all_storage) => {
    const iitc_settings = {};

    // An array of predefined keys for IITC settings
    const storage_keys = ['channel', 'network_host', 'release_update_check_interval', 'beta_update_check_interval', 'custom_update_check_interval'];

    // Loop through all_storage and check if the keys are present in storage_keys
    // If present, add them to the iitc_settings object
    for (const key in all_storage) {
        if (storage_keys.includes(key) && isSet(all_storage[key])) {
            iitc_settings[key] = all_storage[key];
        }
    }
    return iitc_settings;
};

/**
 * Exports specific plugin settings from the provided storage object.
 *
 * This function takes a storage object and extracts plugin settings that have keys starting
 * with the prefix 'VMin'. It creates a new object containing only the plugin settings
 * and returns it.
 *
 * @param {Object} all_storage - The storage object containing all data.
 * @returns {Object} An object containing specific plugin settings.
 */
export const exportPluginsSettings = (all_storage) => {
    const plugins_storage = {};

    // Loop through all_storage and check if the keys start with the prefix 'VMin'
    // If so, add them to the plugins_storage object
    for (const key in all_storage) {
        if (key.startsWith('VMin')) {
            plugins_storage[key] = all_storage[key];
        }
    }
    return plugins_storage;
};

/**
 * Exports external IITC core and plugins from the provided storage object.
 *
 * This function takes a storage object and extracts external IITC core and plugins based on predefined keys.
 * It creates a new object containing the external plugins organized by their channels and filenames,
 * and returns it.
 *
 * @param {Object} all_storage - The storage object containing all data.
 * @returns {Object} An object containing external plugins organized by channels and filenames.
 */
export const exportExternalPlugins = (all_storage) => {
    const external_plugins = {};

    // An array of predefined keys for external plugins
    const storage_keys = [
        'release_iitc_core_user',
        'beta_iitc_core_user',
        'custom_iitc_core_user',
        'release_plugins_user',
        'beta_plugins_user',
        'custom_plugins_user',
    ];

    // Loop through all_storage and check if the keys are present in storage_keys
    // If present, process and add the external plugins to the external_plugins object
    for (const key in all_storage) {
        if (storage_keys.includes(key)) {
            // Extract the channel name from the key by splitting at '_'
            const channel = key.split('_')[0];
            const variant = key.split('_')[1];

            // Create a channel if it doesn't exist
            if (!(channel in external_plugins)) {
                external_plugins[channel] = {};
            }

            // Add a custom IITC core to the external_plugins object
            if (variant === 'iitc' && isSet(all_storage[key]) && isSet(all_storage[key]['code'])) {
                const plugin_filename = 'total-conversion-build.user.js';
                external_plugins[channel][plugin_filename] = all_storage[key]['code'];
                continue;
            }

            // Loop through each plugin UID in the current key's storage data
            for (const plugin_uid in all_storage[key]) {
                // Get the plugin's filename and code from the storage data and add to the external_plugins object
                let plugin_filename = all_storage[key][plugin_uid]['filename'];
                if (!plugin_filename) {
                    plugin_filename = sanitizeFileName(`${all_storage[key][plugin_uid]['name']}.user.js`);
                }
                external_plugins[channel][plugin_filename] = all_storage[key][plugin_uid]['code'];
            }
        }
    }

    return external_plugins;
};

/**
 * Imports IITC settings from the provided backup object.
 *
 * @async
 * @param {Object} self - IITC manager object.
 * @param {Object} backup - The backup object containing IITC settings to import.
 * @returns {Promise<void>} A promise that resolves when the import is complete.
 */
export const importIitcSettings = async (self, backup) => {
    const backup_obj = Object.assign({}, backup);
    const default_channel = self.channel;

    // Set the IITC settings from the backup object into the storage
    await self.storage.set(backup_obj);

    // Check if the channel in the backup object is different from the original channel
    const set_channel = backup_obj.channel;
    if (set_channel !== default_channel) {
        await self.setChannel(set_channel);
    }
};

/**
 * Imports plugin settings from the provided backup object.
 *
 * The function first retrieves all data from the storage object
 * using `self.storage.get(null)` and filters out the records with keys starting with 'VMin'
 * to create a new object `vMinRecords` containing only plugin-related data. The function then
 * merges the `vMinRecords` object with the provided backup object using the `deepmerge` library,
 * resulting in a new storage object `new_storage` that contains updated plugin settings. Finally,
 * the updated storage object is set into the 'self' object using `self.storage.set()`.
 *
 * @async
 * @param {Object} self - IITC manager object.
 * @param {Object} backup - The backup object containing plugin settings to import.
 * @returns {Promise<void>} A promise that resolves when the import is complete.
 */
export const importPluginsSettings = async (self, backup) => {
    const all_storage = await self.storage.get(null);

    // Create a new object containing only plugin-related data (keys starting with 'VMin')
    const vMinRecords = {};
    Object.keys(all_storage).forEach((key) => {
        if (key.startsWith('VMin')) {
            vMinRecords[key] = all_storage[key];
        }
    });

    // Merge the 'vMinRecords' object with the provided backup object and set into storage
    const new_storage = deepmerge(vMinRecords, backup);
    await self.storage.set(new_storage);
};

/**
 * Imports external plugins from the provided backup object.
 *
 * The function iterates through each channel in the backup object,
 * sets the current channel using `self.setChannel()`, and then extracts the plugin information
 * (metadata and code) for each plugin in the channel. The plugin information is added to the 'scripts'
 * array, which is then passed to `self.addUserScripts()` to add the external plugins. After processing
 * all channels, the function sets the default channel using `self.setChannel()` if it was changed during
 * the import process.
 *
 * @async
 * @param {Object} self - IITC manager object.
 * @param {Object} backup - The backup object containing external plugins to import.
 * @returns {Promise<void>} A promise that resolves when the import is complete.
 */
export const importExternalPlugins = async (self, backup) => {
    const default_channel = self.channel;

    // Iterate through each channel in the backup object
    for (const channel of Object.keys(backup)) {
        // Initialize an empty array to store the plugin information (metadata and code)
        const scripts = [];
        await self.setChannel(channel);

        // Iterate through each plugin in the current channel and extract plugin information
        for (const [filename, code] of Object.entries(backup[channel])) {
            // Parse the metadata from the plugin code using the 'parseMeta()' function
            const meta = parseMeta(code);
            meta['filename'] = filename;

            // Push the plugin information (metadata and code) to the 'scripts' array
            scripts.push({ meta: meta, code: code });
        }

        // Add the external plugins using the 'self.addUserScripts()' method
        await self.addUserScripts(scripts);
    }

    // If the current channel is different from the default channel,
    // set the default channel using the 'self.setChannel()' method
    if (self.channel !== default_channel) {
        await self.setChannel(default_channel);
    }
};