import Gio from 'gi://Gio';
import GLib from 'gi://GLib';

import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import * as MessageTray from 'resource:///org/gnome/shell/ui/messageTray.js';

const DONATION_REMINDER_CONSTS = {
    logPrefix: 'Donation Reminder',
    settingsSchema: 'io.stillhq.quicksetup',
    enabledKey: 'donation-reminder-enabled',
    unixTimeKey: 'donation-reminder-unix-time',
    notificationTitle: 'Support stillOS',
    notificationBody: 'stillOS is primarily developed by a college student working another job on top of stillOS. Help make stillOS his full time work.',
    notificationSourceTitle: 'stillOS',
    notificationIconName: 'emblem-favorite-symbolic',
    patreonUrl: 'https://patreon.com/cw/stillHQ',
    periodicCheckSeconds: 60,
    remindLaterDelaySeconds: 7 * 24 * 60 * 60,
};

function getCurrentUnixTimeSeconds() {
    return Math.floor(Date.now() / 1000);
}

export default class DonationReminder {
    enable() {
        this._settings = null;
        this._source = null;
        this._sourceIsCustom = false;
        this._notification = null;
        this._periodicCheckSourceId = null;
        this._screenShieldActiveChangedId = null;
        this._enabledChangedId = null;
        this._unixTimeChangedId = null;

        try {
            this._settings = new Gio.Settings({ schema_id: DONATION_REMINDER_CONSTS.settingsSchema });
        } catch (e) {
            log(`${DONATION_REMINDER_CONSTS.logPrefix}: Failed to load settings schema: ${e}`);
            return;
        }

        this._enabledChangedId = this._settings.connect(
            `changed::${DONATION_REMINDER_CONSTS.enabledKey}`,
            () => this._checkReminder()
        );
        this._unixTimeChangedId = this._settings.connect(
            `changed::${DONATION_REMINDER_CONSTS.unixTimeKey}`,
            () => this._checkReminder()
        );

        this._periodicCheckSourceId = GLib.timeout_add_seconds(
            GLib.PRIORITY_DEFAULT,
            DONATION_REMINDER_CONSTS.periodicCheckSeconds,
            () => {
                this._checkReminder();
                return GLib.SOURCE_CONTINUE;
            }
        );

        if (Main.screenShield?.connect) {
            try {
                this._screenShieldActiveChangedId = Main.screenShield.connect('active-changed', () => {
                    if (Main.screenShield?.active)
                        return;

                    this._checkReminder();
                });
            } catch (e) {
                log(`${DONATION_REMINDER_CONSTS.logPrefix}: Failed to monitor unlock events: ${e}`);
            }
        }

        this._checkReminder();
    }

    _checkReminder() {
        if (!this._settings)
            return;

        let reminderEnabled = false;
        let reminderUnixTime = 0;

        try {
            reminderEnabled = this._settings.get_boolean(DONATION_REMINDER_CONSTS.enabledKey);
            reminderUnixTime = Number(this._settings.get_int64(DONATION_REMINDER_CONSTS.unixTimeKey));
        } catch (e) {
            log(`${DONATION_REMINDER_CONSTS.logPrefix}: Failed reading reminder state: ${e}`);
            return;
        }

        if (!reminderEnabled || reminderUnixTime === 0)
            return;

        if (getCurrentUnixTimeSeconds() < reminderUnixTime)
            return;

        if (this._showReminderNotification())
            this._clearReminder();
    }

    _showReminderNotification() {
        let notification;

        try {
            const source = this._getNotificationSource();

            notification = new MessageTray.Notification({
                source,
                title: DONATION_REMINDER_CONSTS.notificationTitle,
                body: DONATION_REMINDER_CONSTS.notificationBody,
            });
            notification.resident = true;
            notification.iconName = DONATION_REMINDER_CONSTS.notificationIconName;
            notification.urgency = MessageTray.Urgency.CRITICAL;

            notification.addAction('No Thanks', () => {
                notification.destroy();
            });
            notification.addAction('Remind Me Later', () => {
                this._scheduleReminder(
                    getCurrentUnixTimeSeconds() + DONATION_REMINDER_CONSTS.remindLaterDelaySeconds
                );
                notification.destroy();
            });
            notification.addAction('Support on Patreon', () => {
                this._openPatreonPage();
                notification.destroy();
            });

            notification.connect('destroy', () => {
                if (this._notification === notification)
                    this._notification = null;
            });

            this._notification = notification;
            source.addNotification(notification);
            return true;
        } catch (e) {
            if (notification)
                notification.destroy();

            log(`${DONATION_REMINDER_CONSTS.logPrefix}: Failed to show reminder notification: ${e}`);
            return false;
        }
    }

    _getNotificationSource() {
        if (this._source)
            return this._source;

        if (MessageTray.getSystemSource) {
            this._source = MessageTray.getSystemSource();
        } else {
            this._source = new MessageTray.Source({
                title: DONATION_REMINDER_CONSTS.notificationSourceTitle,
                iconName: DONATION_REMINDER_CONSTS.notificationIconName,
            });
            this._sourceIsCustom = true;
            Main.messageTray.add(this._source);
        }

        this._source.title = DONATION_REMINDER_CONSTS.notificationSourceTitle;
        this._source.iconName = DONATION_REMINDER_CONSTS.notificationIconName;

        return this._source;
    }

    _openPatreonPage() {
        try {
            Gio.AppInfo.launch_default_for_uri(DONATION_REMINDER_CONSTS.patreonUrl, null);
        } catch (e) {
            log(`${DONATION_REMINDER_CONSTS.logPrefix}: Failed to open Patreon page: ${e}`);
        }
    }

    _clearReminder() {
        this._scheduleReminder(0, false);
    }

    _scheduleReminder(unixTime, enabled = true) {
        if (!this._settings)
            return;

        try {
            this._settings.set_boolean(DONATION_REMINDER_CONSTS.enabledKey, enabled);
            this._settings.set_int64(DONATION_REMINDER_CONSTS.unixTimeKey, unixTime);
        } catch (e) {
            log(`${DONATION_REMINDER_CONSTS.logPrefix}: Failed updating reminder state: ${e}`);
        }
    }

    disable() {
        if (this._periodicCheckSourceId) {
            GLib.source_remove(this._periodicCheckSourceId);
            this._periodicCheckSourceId = null;
        }

        if (this._notification) {
            this._notification.destroy();
            this._notification = null;
        }

        if (this._settings && this._enabledChangedId) {
            this._settings.disconnect(this._enabledChangedId);
            this._enabledChangedId = null;
        }

        if (this._settings && this._unixTimeChangedId) {
            this._settings.disconnect(this._unixTimeChangedId);
            this._unixTimeChangedId = null;
        }

        if (this._screenShieldActiveChangedId && Main.screenShield?.disconnect) {
            Main.screenShield.disconnect(this._screenShieldActiveChangedId);
            this._screenShieldActiveChangedId = null;
        }

        if (this._sourceIsCustom && this._source?.destroy) {
            this._source.destroy();
            this._source = null;
            this._sourceIsCustom = false;
        } else {
            this._source = null;
        }

        this._settings = null;
    }
}
