/*
 * Queue - A Queueing system that can be used to handle labs in higher education
 * Copyright (C) 2016-2024  Delft University of Technology
 *
 * This program 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/>.
 */
"use strict";

$(function () {
    if ("serviceWorker" in navigator) {
        navigator.serviceWorker.register("/sw.js").then(initialiseState);
    } else {
        console.warn("Service workers are not supported in this browser.");
    }
});

/**
 * Initializes the web push state.
 */
function initialiseState() {
    // Check whether the browser supports showing a notification in the first place
    if (!("showNotification" in ServiceWorkerRegistration.prototype)) {
        console.warn('Notifications aren"t supported.');
        return;
    }

    // Check whether PushManager from the Push API is available in this browser
    if (!("PushManager" in window)) {
        console.warn('Push messaging isn"t supported.');
        return;
    }

    // Check whether the user has already given permission for push notifications
    if (Notification.permission === "denied") {
        console.warn("The user has blocked notifications.");
        return;
    }

    // Ask the user for permission and send a message to the service worker to register the subscription.
    Notification.requestPermission().then(permission => {
        if (permission === "granted") {
            subscribe();
        }
    });
}

/**
 * Subscribes the browser to a push service and sends the subscription to our (Spring) application server.
 */
function subscribe() {
    // Gets and registers a push subscription if necessary
    navigator.serviceWorker.ready.then(serviceWorkerRegistration => {
        const pm = serviceWorkerRegistration.pushManager;
        pm.getSubscription()
            .then(subscription => {
                if (!subscription) {
                    return fetch("/push/public-vapid-key")
                        .then(response => response.text())
                        .then(vapid =>
                            pm.subscribe({
                                userVisibleOnly: true,
                                applicationServerKey: urlBase64ToUint8Array(vapid),
                            })
                        );
                }
                return Promise.resolve(subscription);
            })
            .then(sendSubscriptionToServer)
            .catch(function (err) {
                console.warn("An error occurred while subscribing for push notifications", err);
            });
    });
}

/**
 * Sends the push subscription to our (Spring) application server so that they can push back to the browser.
 * @param subscription The subscription to send to Queue.
 */
function sendSubscriptionToServer(subscription) {
    console.info(`Sending push subscription information to Queue: ${subscription.endpoint}`);

    const key = subscription.getKey ? subscription.getKey("p256dh") : "";
    const auth = subscription.getKey ? subscription.getKey("auth") : "";

    $.post("/push/subscribe", {
        endpoint: subscription.endpoint,
        p256dhKey: key ? btoa(String.fromCharCode.apply(null, new Uint8Array(key))) : "",
        auth: auth ? btoa(String.fromCharCode.apply(null, new Uint8Array(auth))) : "",
    });
}

/**
 * urlBase64ToUint8Array
 *
 * @param {string} base64String a public vapid key
 */
function urlBase64ToUint8Array(base64String) {
    const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
    const base64 = (base64String + padding).replace(/\-/g, "+").replace(/_/g, "/");

    const rawData = window.atob(base64);
    const outputArray = new Uint8Array(rawData.length);

    for (let i = 0; i < rawData.length; ++i) {
        outputArray[i] = rawData.charCodeAt(i);
    }
    return outputArray;
}
