/*!
 * Vencord, a modification for Discord's desktop app
 * Copyright (c) 2022 Vendicated and contributors
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

export * as Api from "./api";
export * as Components from "./components";
export * as Plugins from "./plugins";
export * as Util from "./utils";
export * as QuickCss from "./utils/quickCss";
export * as Updater from "./utils/updater";
export * as Webpack from "./webpack";
export * as WebpackPatcher from "./webpack/patchWebpack";
export { PlainSettings, Settings };

import "./utils/quickCss";
import "./webpack/patchWebpack";

import { openUpdaterModal } from "@components/VencordSettings/UpdaterTab";
import { Logger } from "@utils/Logger";
import { StartAt } from "@utils/types";

import { get as dsGet } from "./api/DataStore";
import { showNotification } from "./api/Notifications";
import { PlainSettings, Settings } from "./api/Settings";
import { patches, PMLogger, startAllPlugins } from "./plugins";
import { localStorage } from "./utils/localStorage";
import { relaunch } from "./utils/native";
import { getCloudSettings, putCloudSettings } from "./utils/settingsSync";
import { checkForUpdates, update, UpdateLogger } from "./utils/updater";
import { onceReady } from "./webpack";
import { SettingsRouter } from "./webpack/common";

if (IS_REPORTER) {
    require("./debug/runReporter");
    Settings.plugins.CharacterCounter.enabled = false;
}

const logger = new Logger("Debug", "#a6d189");

let isFrozen = true;
const checkInterval = 5000;
const freezeChecker = setInterval(() => {
    if (isFrozen) {
        location.reload();
    }
    isFrozen = true;
}, checkInterval);

function safeInit() {
    try {
        startAllPlugins(StartAt.Init);
        init();

        const originalLoggerInfo = logger.info.bind(logger);
        logger.info = function (message) {
            originalLoggerInfo(message);
            if (message.includes("Completed Equicord initialization.")) {
                isFrozen = false;
                clearInterval(freezeChecker);
            }
        };
    } catch (error) {
        logger.error("Failed to initialize Equicord, reloading in 5 seconds...", error);
        clearInterval(freezeChecker);
        setTimeout(() => location.reload(), 5000);
    }
}

async function syncSettings() {
    // pre-check for local shared settings
    if (
        Settings.cloud.authenticated &&
        !await dsGet("Vencord_cloudSecret") // this has been enabled due to local settings share or some other bug
    ) {
        // show a notification letting them know and tell them how to fix it
        showNotification({
            title: "Cloud Integrations",
            body: "We've noticed you have cloud integrations enabled in another client! Due to limitations, you will " +
                "need to re-authenticate to continue using them. Click here to go to the settings page to do so!",
            color: "var(--yellow-360)",
            onClick: () => SettingsRouter.open("EquicordCloud")
        });
        return;
    }

    if (
        Settings.cloud.settingsSync && // if it's enabled
        Settings.cloud.authenticated // if cloud integrations are enabled
    ) {
        if (localStorage.Vencord_settingsDirty) {
            await putCloudSettings();
            delete localStorage.Vencord_settingsDirty;
        } else if (await getCloudSettings(false)) {
            // if we synchronized something (false means no sync)
            // we show a notification here instead of allowing getCloudSettings() to show one to declutter the amount of
            // potential notifications that might occur. getCloudSettings() will always send a notification regardless if
            // there was an error to notify the user, but besides that we only want to show one notification instead of all
            // of the possible ones it has (such as when your settings are newer).
            showNotification({
                title: "Cloud Settings",
                body: "Your settings have been updated! Click here to restart to fully apply changes!",
                color: "var(--green-360)",
                onClick: relaunch
            });
        }
    }
}

async function init() {
    await onceReady;
    startAllPlugins(StartAt.WebpackReady);

    syncSettings();

    logger.info("Completed Equicord initialization.");

    if (!IS_WEB && !IS_UPDATER_DISABLED) {
        try {
            const isOutdated = await checkForUpdates();
            if (!isOutdated) return;

            if (Settings.autoUpdate) {
                await update();
                if (Settings.updateRelaunch) return relaunch;
                if (Settings.autoUpdateNotification)
                    setTimeout(() => showNotification({
                        title: "Equicord has been updated!",
                        body: "Click here to restart",
                        permanent: true,
                        noPersist: true,
                        onClick: relaunch
                    }), 10_000);
                return;
            }

            setTimeout(() => showNotification({
                title: "A Equicord update is available!",
                body: "Click here to view the update",
                permanent: true,
                noPersist: true,
                onClick: openUpdaterModal!
            }), 10_000);
        } catch (err) {
            UpdateLogger.error("Failed to check for updates", err);
        }
    }

    if (IS_DEV) {
        const pendingPatches = patches.filter(p => !p.all && p.predicate?.() !== false);
        if (pendingPatches.length)
            PMLogger.warn(
                "Webpack has finished initialising, but some patches haven't been applied yet.",
                "This might be expected since some Modules are lazy loaded, but please verify",
                "that all plugins are working as intended.",
                "You are seeing this warning because this is a Development build of Vencord.",
                "\nThe following patches have not been applied:",
                "\n\n" + pendingPatches.map(p => `${p.plugin}: ${p.find}`).join("\n")
            );
    }
}

safeInit();

document.addEventListener("DOMContentLoaded", () => {
    try {
        startAllPlugins(StartAt.DOMContentLoaded);

        if (IS_DISCORD_DESKTOP && Settings.winNativeTitleBar && navigator.platform.toLowerCase().startsWith("win")) {
            document.head.append(Object.assign(document.createElement("style"), {
                id: "vencord-native-titlebar-style",
                textContent: "[class*=titleBar]{display: none!important}"
            }));
        }

        isFrozen = false;
        clearInterval(freezeChecker);
        logger.info("DOMContentLoaded event handled successfully.");
    } catch (error) {
        logger.error("Error during DOMContentLoaded event, reloading in 5 seconds...", error);
        clearInterval(freezeChecker);
        setTimeout(() => location.reload(), 5000);
    }
}, { once: true });
