/**
 * Copyright (C) 2024 Puter Technologies Inc.
 *
 * This file is part of Puter.
 *
 * Puter is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

window.puter_gui_enabled = true;
/**
 * Initializes and configures the GUI (Graphical User Interface) settings based on the provided options.
 * 
 * The function sets global variables in the window object for various settings such as origins and domain names.
 * It also handles loading different resources depending on the environment (development or production).
 * 
 * @param {Object} options - Configuration options to initialize the GUI.
 * @param {string} [options.gui_origin='https://puter.com'] - The origin URL for the GUI.
 * @param {string} [options.api_origin='https://api.puter.com'] - The origin URL for the API.
 * @param {number} [options.max_item_name_length=500] - Maximum allowed length for an item name.
 * @param {boolean} [options.require_email_verification_to_publish_website=true] - Flag to decide whether email verification is required to publish a website.
 * 
 * @property {string} [options.app_domain] - Extracted domain name from gui_origin. It's derived automatically if not provided.
 * @property {string} [window.gui_env] - The environment in which the GUI is running (e.g., "dev" or "prod").
 * 
 * @returns {Promise<void>} Returns a promise that resolves when initialization and resource loading are complete.
 * 
 * @example
 * window.gui({
 *     gui_origin: 'https://myapp.com',
 *     api_origin: 'https://myapi.com',
 *     max_item_name_length: 250
 * });
 */

window.gui = async function(options){
    options = options ?? {};
    // app_origin is deprecated, use gui_origin instead
    window.gui_origin = options.gui_origin ?? options.app_origin ?? `https://puter.com`;
    window.app_domain = options.app_domain ?? new URL(window.gui_origin).hostname;
    window.hosting_domain = options.hosting_domain ?? 'puter.site';
    window.api_origin = options.api_origin ?? "https://api.puter.com";
    window.max_item_name_length = options.max_item_name_length ?? 500;
    window.require_email_verification_to_publish_website = options.require_email_verification_to_publish_website ?? true;

    // Add Puter.JS
    await loadScript('https://js.puter.com/v2/');

    // DEV: Load the initgui.js file if we are in development mode
    if(!window.gui_env || window.gui_env === "dev"){
        await loadScript('/initgui.js', {isModule: true});
    }
    
    // PROD: load the minified bundles if we are in production mode
    // note: the order of the bundles is important
    // note: Build script will prepend `window.gui_env="prod"` to the top of the file
    else if(gui_env === "prod"){
        // Load the minified bundles
        await loadCSS('/dist/bundle.min.css');
        await loadScript('/dist/bundle.min.js');
    }

    // 🚀 Launch the GUI 🚀
    initgui();
}

/**
* Dynamically loads an external JavaScript file.
* @param {string} url The URL of the external script to load.
* @param {Object} [options] Optional configuration for the script. 
* @param {boolean} [options.isModule] Whether the script is a module.
* @param {boolean} [options.defer] Whether the script should be deferred.
* @param {Object} [options.dataAttributes] An object containing data attributes to add to the script element.
* @returns {Promise} A promise that resolves once the script has loaded, or rejects on error.
*/
window.loadScript = async function(url, options = {}) {
    return new Promise((resolve, reject) => {
        const script = document.createElement('script');
        script.src = url;

        // Set default script loading behavior
        script.async = true;

        // Handle if it is a module
        if (options.isModule) {
            script.type = 'module';
        }

        // Handle defer attribute
        if (options.defer) {
            script.defer = true;
            script.async = false; // When "defer" is true, "async" should be false as they are mutually exclusive
        }

        // Add arbitrary data attributes
        if (options.dataAttributes && typeof options.dataAttributes === 'object') {
            for (const [key, value] of Object.entries(options.dataAttributes)) {
                script.setAttribute(`data-${key}`, value);
            }
        }

        // Resolve the promise when the script is loaded
        script.onload = () => resolve();

        // Reject the promise if there's an error during load
        script.onerror = (error) => reject(new Error(`Failed to load script at url: ${url}`));

        // Append the script to the body
        document.body.appendChild(script);
    });
};

/**
* Dynamically loads an external CSS file.
* @param {string} url The URL of the external CSS to load.
* @returns {Promise} A promise that resolves once the CSS has loaded, or rejects on error.
*/
window.loadCSS = async function(url) {
    return new Promise((resolve, reject) => {
        const link = document.createElement('link');
        link.rel = 'stylesheet';
        link.href = url;

        link.onload = () => {
            resolve();
        };

        link.onerror = (error) => {
            reject(new Error(`Failed to load CSS at url: ${url}`));
        };

        document.head.appendChild(link);
    });
}