/**
 * @name AutoStartRichPresence
 * @version 2.0.20
 *
 * @author Miniontoby
 * @authorId 849180136828960799
 * @description Auto starts Rich Presence with configurable settings.
 *
 * @updateUrl https://raw.githubusercontent.com/Miniontoby/MinionBDStuff/main/Plugins/AutoStartRichPresence/AutoStartRichPresence.plugin.js
 * @source https://raw.githubusercontent.com/Miniontoby/MinionBDStuff/main/Plugins/AutoStartRichPresence/AutoStartRichPresence.plugin.js
 * @website https://github.com/Miniontoby/MinionBDStuff/tree/main/Plugins/AutoStartRichPresence/
 */

// Updated Jan 24th, 2026

/*@cc_on
@if (@_jscript)
    // Offer to self-install for clueless users that try to run this directly.
    var shell = WScript.CreateObject("WScript.Shell");
    var fs = new ActiveXObject("Scripting.FileSystemObject");
    var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\\BetterDiscord\\plugins");
    var pathSelf = WScript.ScriptFullName;
    // Put the user at ease by addressing them in the first person
    shell.Popup("It looks like you mistakenly tried to run me directly. (don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30);
    if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) {
        shell.Popup("I'm in the correct folder already.\nJust reload Discord with Ctrl+R.", 0, "I'm already installed", 0x40);
    } else if (!fs.FolderExists(pathPlugins)) {
        shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10);
    } else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) {
        fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true);
        // Show the user where to put plugins in the future
        shell.Exec("explorer " + pathPlugins);
        shell.Popup("I'm installed!\nJust reload Discord with Ctrl+R.", 0, "Successfully installed", 0x40);
    }
    WScript.Quit();
@else @*/


const config = {
    changelog: [
        {
            title: "Update 2.0.20 - Jan 24th, 2026",
            type: "improved",
            items: [
                "Fixed the this.rpc variable being undefined due to Discord updating. It now uses getStore instead",
                "Made some more statement break less when unpredictable variables become undefined"
            ]
        },
        {
            title: "Update 2.0.19 - May 4th, 2025",
            type: "improved",
            items: [
                "Fixed activity data not being set to undefined if empty",
                "Fixed bug #3 where activity name causes crash when showing yourself in sidebar"
            ]
        },
        {
            title: "Update 2.0.18 - May 1st, 2025",
            type: "improved",
            items: [
                "Fixed the timestamp format since it had to be in miliseconds"
            ]
        },
        {
            title: "Update 2.0.17 - April 24th, 2025",
            type: "improved",
            items: [
                "Updated the code to make the settings work when there's no profiles"
            ]
        },
        {
            title: "Update 2.0.16 - April 5th, 2025",
            type: "improved",
            items: [
                "Updated the code to not use deprecated functions - issue #2",
                "New logging system, so there's always a prefix",
                "Using this.api. instead of BdApi.",
                "Moved around some toasts to make them show when the plugin actually started up or shutdown"
            ]
        },
        {
            title: "Update 2.0.15 - March 29th, 2025",
            type: "improved",
            items: [
                "Upgraded to newer BD 1.11.0 version",
                "Added changelog"
            ]
        },
    ],
    // config is not static, but dynamic. Not specified here.
};

const DEFAULT_CLIENT_ID = '1002618051608444958';

function isURL(url) {
    try {
        const newUrl = new URL(url);
        return newUrl.protocol === 'http:' || newUrl.protocol === 'https:';
    } catch (e) {
        return false;
    }
}

class AutoStartRichPresence {
    constructor(meta) {
        this.meta = meta;
        this.api = new BdApi(this.meta.name);

        this.initialized = false;
        this.settings = {};
        this.startPlaying = Date.now();
        this.updateDataInterval = 0;
        this.rpc = {};

        let filter = this.api.Webpack.Filters.byStrings("getAssetImage: size must === [number, number] for Twitch");
        let assetManager = this.api.Webpack.getModule(m => typeof m === "object" && Object.values(m).some(filter)) ?? [];
        let getAsset;
        for (const key in assetManager) {
            const member = assetManager[key];
            if (member.toString().includes("APPLICATION_ASSETS_FETCH")) { // find the fetchAssetIds
                getAsset = member;
                break;
            }
        }
        this.getAsset = async key => {
            const clientID = !this.isNullOrEmpty(this.activeProfile.clientID) && this.activeProfile.clientID || DEFAULT_CLIENT_ID;
            if (getAsset) return (await getAsset(clientID, [key, undefined]))[0];
            else return "";
        };
    }
    async start() {
        this.initialize();

        const savedVersion = this.api.Data.load("version");
        if (savedVersion !== this.meta.version) {
            this.api.UI.showChangelogModal({
                title: this.meta.name,
                subtitle: this.meta.version,
                blurb: "Automatic customizable rich presence status plugin",
                changes: config.changelog
            });
            this.api.Data.save("version", this.meta.version);
        }
    }
    initialize() {
        this.api.Logger.log("Starting...");
        this.updateDataInterval = setInterval(() => this.updateData(), 60*1000); // every 60 seconds
        this.updateSettingsTimeout = -1;

        this.settings = this.api.Data.load("settings") || {};
        if (this.settings.clientID || this.settings.details || this.settings.state) this.migrateData();

        this.profiles = this.api.Data.load("profiles") || [];
        if (!this.settings.activeProfileID && this.profiles.length) this.settings.activeProfileID = 0;

        this.getLocalPresence = this.api.Webpack.getModule(this.api.Webpack.Filters.byKeys("getLocalPresence"))?.getLocalPresence;
        this.rpc = this.api.Webpack.getStore("UserStore")?._dispatcher;
        this.rpcClientInfo = {};
        this.discordSetActivityHandler = null;
        this.initialized = true;
        this.updateData();
        this.api.Logger.log("Started!");
        this.api.UI.showToast("AutoStartRichPresence has started!");
    }
    async stop() {
        this.api.Logger.log("Stopping...");
        clearInterval(this.updateDataInterval);
        this.updateDataInterval = 0;
        this.initialized = false;
        this.setActivity({});
        this.api.Logger.log("Stopped");
        this.api.UI.showToast("AutoStartRichPresence has stopped!");
    }
    get activeProfile() {
        if (!this.profiles?.length || this.profiles?.length == 0) return {};
        return this.profiles[this.settings.activeProfileID];
    }
    getSettingsPanel() {
        if (!this.initialized) return;
        this.settings = this.api.Data.load("settings") || {};
        this.profiles = this.api.Data.load("profiles") || [];

        const settings = [
            ...(this.profiles.length > 0 ? 
            [{
                type: "dropdown",
                id: "ASRPActiveProfileSelector",
                name: "Select Active Profile",
                note: "With this plugin you can have multiple presets.",
                value: this.settings.activeProfileID ?? 0,
                options: this.profiles.map((prof, i) => { return { value: i, label: prof.pname }; }),
            }] : []),
            { type: "switch", id: "disableWhenActivity", name: "Disable When Activity", note: "Disables when there is another activity", value: !!this.settings.disableWhenActivity },
            {
                type: "button",
                id: "createProfile",
                children: "Create New Profile",
                note: "Close and reopen the settings window afterwards!",
                inline: false,
                onClick: () => {
                    this.profiles.push({ pname: "New Profile" });
                    if (this.profiles.length === 1) this.settings.activeProfileID = 0;
                    this.updateProfiles();
                    this.api.UI.showToast("[AutoStartRichPresence] Created a new profile", { type: "success" });
                },
            },
        ];
        for (const i in this.profiles) {
            const profile = this.profiles[i];
            settings.push({
                type: "category",
                id: "profile_" + i,
                name: "Edit Profile - " + profile.pname,
                collapsible: true,
                shown: false,
                settings: [
                    { type: "text", id: "pname", name: "Profile Name", note: "Enter a profile name (this doesn't show up on discord!) [Close and reopen the settings menu afterwards!]", value: profile.pname },
                    { type: "text", id: "clientID", name: "Client ID", note: "Enter your Client ID (get from developers page) [needed for image keys]", value: profile.clientID },
                    { type: "text", id: "name", name: "Activity Name", note: "Enter a name for the activity", value: profile.name },
                    { type: "text", id: "details", name: "Activity Details", note: "Enter a description for the activity", value: profile.details },
                    { type: "text", id: "state", name: "Activity State", note: "Enter a second description for the activity", value: profile.state },
                    { type: "text", id: "button1Label", name: "Activity Button 1 Text", note: "Enter Text for button 1", value: profile.button1Label },
                    { type: "text", id: "button1URL", name: "Activity Button 1 URL", note: "Enter URL for button 1", value: profile.button1URL },
                    { type: "text", id: "button2Label", name: "Activity Button 2 Text", note: "Enter Text for button 2", value: profile.button2Label },
                    { type: "text", id: "button2URL", name: "Activity Button 2 URL", note: "Enter URL for button 2", value: profile.button2URL },
                    { type: "text", id: "smallImageKey", name: "Activity Small Image Key", note: "Enter Image Key for Small Icon", value: profile.smallImageKey },
                    { type: "text", id: "smallImageText", name: "Activity Small Image Text", note: "Enter Label for Small Icon", value: profile.smallImageText },
                    { type: "text", id: "largeImageKey", name: "Activity Large Image Key", note: "Enter Image Key for Large Icon", value: profile.largeImageKey },
                    { type: "text", id: "largeImageText", name: "Activity Large Image Text", note: "Enter Label for Large Icon", value: profile.largeImageText },
                    { type: "switch", id: "enableStartTime", name: "Enable Start Time", note: "Enable timestamp which shows the time when started", value: profile.enableStartTime },
                    { type: "switch", id: "listeningTo", name: "Listening Status", note: "Enable listening status", value: profile.listeningTo },
                    {
                        type: "button",
                        id: "deleteProfile",
                        children: "Delete Profile",
                        note: "Close and reopen the settings window after!",
                        inline: false,
                        onClick: () => {
                            const profileID = i;
                            if (profileID >= this.profiles.length) return this.api.Logger.log("profileSettings too high ID", profileID, id, value);
                            this.api.UI.showConfirmationModal("Delete Rich Presence Profile", `Are you sure you want to delete ${this.profiles[profileID]?.pname || "this profile"}? (This will not delete any Discord Developer Applications.)`, {
                                danger: true,
                                confirmText: "Delete",
                                onConfirm: () => {
                                    this.deleteProfile(profileID);
                                    this.updateProfiles();
                                    this.api.UI.showToast("[AutoStartRichPresence] Deleted profile", { type: "success" });
                                }
                            });
                        },
                        color: this.api.Components.Button.Colors.RED,
                    },
                ],
            });
        }

        return this.api.UI.buildSettingsPanel({
            settings: settings,
            onChange: (category, id, value) => {
                if (category === null) {
                    if (id === "ASRPActiveProfileSelector") {
                        if (isNaN(value)) return;
                        this.settings.activeProfileID = value;
                        this.updateSettings();
                    } else if (id === "disableWhenActivity") {
                        if (value !== true && value !== false) return;
                        this.settings.disableWhenActivity = value;
                        this.updateSettings();
                    } else {
                        this.api.Logger.log("globalSettings UNKNOWN ID", id, value);
                    }
                } else if (category.startsWith("profile_")) {
                    const profileID = Number(category.replace("profile_", ""));
                    if (isNaN(profileID)) return this.api.Logger.log("profileSettings NOT A NUMBER", category, profileID, id, value);
                    if (profileID >= this.profiles.length) return this.api.Logger.log("profileSettings too high ID", profileID, id, value);
                    //if (!(id in this.profiles[profileID])) return this.api.Logger.log("profileSettings", profileID, "UNKNOWN ID", id, value);
                    this.profiles[profileID][id] = value;
                    this.updateProfiles();
                } else {
                    this.api.Logger.log("UNKNOWN CATEGORY", category, id, value);
                }
            }
        });
    }
    async updateData() {
        if (!this.initialized) return;
        if (this.profiles.length === 0) return this.setActivity({});

        if (this.settings.disableWhenActivity) {
            const activities = (this.getLocalPresence?.()?.activities ?? []).filter(a => a?.application_id !== undefined);
            const clientID = !this.isNullOrEmpty(this.activeProfile.clientID) && this.activeProfile.clientID || DEFAULT_CLIENT_ID;
            if (activities.filter(a => a.application_id !== clientID).length > 0) {
                if (activities.find(a => a.application_id === clientID)) this.setActivity({});
                return;
            }
        }
        setTimeout(() => this.updateRichPresence(), 50);
    }
    setActivity(activity) {
        const obj = activity && (Object.entries(activity).length > 0 && Object.assign(activity, { flags: 1, type: this.activeProfile?.listeningTo ? 2 : 0 }) || activity);
        // this.api.Logger.log(obj);
        if (!this.rpc?.dispatch)
            this.api.Logger.log("this.RPC/Dispatcher object is empty! Not setting activity!");
        else
            this.rpc.dispatch({
                type: "LOCAL_ACTIVITY_UPDATE",
                activity: obj,
            });
    }
    isNullOrEmpty(input) {
        return input === undefined || input === null || input === '';
    }
    async updateRichPresence() {
        if (!this.initialized || !this.activeProfile) return;

        let button_urls = [], buttons = [];
        if (!this.isNullOrEmpty(this.activeProfile.button1Label) && !this.isNullOrEmpty(this.activeProfile.button1URL)) {
            if (this.activeProfile.button1Label.length > 32) this.api.UI.showToast("[AutoStartRichPresence] Button 1 label must not exceed 32 characters.", { type: "error" });
            else if (!isURL(this.activeProfile.button1URL)) this.api.UI.showToast("[AutoStartRichPresence] Invalid button 1 URL.", { type: "error" });
            else {
                buttons.push(this.activeProfile.button1Label);
                button_urls.push(this.activeProfile.button1URL);
            }
        }
        if (!this.isNullOrEmpty(this.activeProfile.button2Label) && !this.isNullOrEmpty(this.activeProfile.button2URL)) {
            if (this.activeProfile.button2Label.length > 32) this.api.UI.showToast("[AutoStartRichPresence] Button 2 label must not exceed 32 characters.", { type: "error" });
            else if (!isURL(this.activeProfile.button2URL)) this.api.UI.showToast("[AutoStartRichPresence] Invalid button 2 URL.", { type: "error" });
            else {
                buttons.push(this.activeProfile.button2Label);
                button_urls.push(this.activeProfile.button2URL);
            }
        }
        if (this.activeProfile.enableStartTime) {
            if (this.startPlaying == null) this.startPlaying = Date.now();
        } else if (this.startPlaying) this.startPlaying = null;

        let obj = {
            application_id: (!this.isNullOrEmpty(this.activeProfile.clientID) && this.activeProfile.clientID) || DEFAULT_CLIENT_ID,
            name: (!this.isNullOrEmpty(this.activeProfile.name) && this.activeProfile.name) || "", // name must be something, else discord will crash
            details: (!this.isNullOrEmpty(this.activeProfile.details) && this.activeProfile.details) || undefined,
            state: (!this.isNullOrEmpty(this.activeProfile.state) && this.activeProfile.state) || undefined,
            timestamps: this.startPlaying ? { start: Math.floor(this.startPlaying) } : undefined,
            assets: (!this.isNullOrEmpty(this.activeProfile.smallImageKey)) ? {
                small_image: await this.getAsset(this.activeProfile.smallImageKey),
                small_text: this.activeProfile.smallImageText ?? undefined,
            } : {},
            metadata: { button_urls }, buttons
        }
        if (!this.isNullOrEmpty(this.activeProfile.largeImageKey)) {
            obj.assets.large_image = await this.getAsset(this.activeProfile.largeImageKey);
            obj.assets.large_text = this.activeProfile.largeImageText ?? undefined;
        }
        this.setActivity(obj);
    }
    updateSettings() {
        this.api.Data.save("settings", this.settings);
        this.updateData(); // will return when not initialized
    }
    updateProfiles() {
        this.api.Data.save("profiles", this.profiles);
        clearTimeout(this.updateSettingsTimeout);
        this.updateSettingsTimeout = setTimeout(() => this.updateData(), 1e3);
    }
    deleteProfile(id) {
        this.profiles.splice(id, 1);
        if (this.settings.activeProfileID === id) {
            this.settings.activeProfileID = 0;
            this.updateSettings();
            this.updateData();
        } else if (this.settings.activeProfileID > id) {
            this.settings.activeProfileID--;
            this.updateSettings();
        }
        this.updateProfiles();
    }
    migrateData() {
        let profilesData = this.api.Data.load("profiles");
        // if (!profilesData?.length || profilesData.length > 0) return;
        this.settings = this.api.Data.load("settings");
        this.api.Logger.log("Migrating your data...");
        this.api.UI.showToast("[AutoStartRichPresence] Migrating your data...");
        this.settings.activeProfileID = 0;
        this.settings.disableWhenActivity = false;
        profilesData = [{
            pname: "My Profile"
        }];
        for (const key of ["clientID", "name", "details", "state", "largeImageKey", "largeImageText", "smallImageKey", "smallImageText", "button1Label", "button1URL", "button2Label", "button2URL", "enableStartTime", "listeningTo"]) {
            profilesData[0][key] = this.settings[key];
            delete this.settings[key];
        }
        this.profiles = profilesData;
        this.updateProfiles();
        this.updateSettings();
        this.api.Logger.log("Migrated your data!");
        this.api.UI.showToast("[AutoStartRichPresence] Migrated your data!", { type: "success" });
    }
}

module.exports = AutoStartRichPresence;

/*@end @*/
