export type PushSubscriptionPayload = {
    endpoint: string;
    keys: {
        p256dh: string;
        auth: string;
    };
};

export const isPushSupported = (): boolean => (
    typeof window !== 'undefined'
    && 'serviceWorker' in navigator
    && 'PushManager' in window
    && 'Notification' in window
);

export const decodeBase64Url = (value: string): Uint8Array<ArrayBuffer> => {
    const padding = '='.repeat((4 - (value.length % 4)) % 4);
    const base64 = (value + padding).replace(/-/g, '+').replace(/_/g, '/');
    const decoded = window.atob(base64);
    const bytes = new Uint8Array(decoded.length);

    for (let index = 0; index < decoded.length; index += 1) {
        bytes[index] = decoded.charCodeAt(index);
    }

    return bytes;
};

const encodeBase64Url = (buffer: ArrayBuffer): string => {
    const bytes = new Uint8Array(buffer);
    let binary = '';

    for (const byte of bytes) {
        binary += String.fromCharCode(byte);
    }

    return window.btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
};

export const getCurrentPushSubscription = async (): Promise<PushSubscription | null> => {
    if (!isPushSupported()) return null;

    const registration = await navigator.serviceWorker.ready;
    return registration.pushManager.getSubscription();
};

export const subscribeCurrentBrowser = async (publicKey: string): Promise<PushSubscription> => {
    if (!isPushSupported()) {
        throw new Error('Dieser Browser unterstützt Push-Benachrichtigungen nicht.');
    }

    const registration = await navigator.serviceWorker.ready;
    const existingSubscription = await registration.pushManager.getSubscription();

    if (existingSubscription) return existingSubscription;

    return registration.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: decodeBase64Url(publicKey),
    });
};

export const serializePushSubscription = (subscription: PushSubscription): PushSubscriptionPayload => {
    const p256dh = subscription.getKey('p256dh');
    const auth = subscription.getKey('auth');

    if (!p256dh || !auth) {
        throw new Error('Die Schlüsseldaten des Browser-Abonnements konnten nicht gelesen werden.');
    }

    return {
        endpoint: subscription.endpoint,
        keys: {
            p256dh: encodeBase64Url(p256dh),
            auth: encodeBase64Url(auth),
        },
    };
};
