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;
}

async function getCsrfToken() {
    return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') ?? '';
}

async function fetchJson(url, options = {}) {
    const headers = {
        Accept: 'application/json',
        'X-Requested-With': 'XMLHttpRequest',
        ...(options.headers || {}),
    };
    if (!headers['X-CSRF-TOKEN'] && !headers['X-XSRF-TOKEN']) {
        headers['X-CSRF-TOKEN'] = await getCsrfToken();
    }
    if (options.body && typeof options.body === 'object' && !(options.body instanceof FormData)) {
        headers['Content-Type'] = 'application/json';
        options.body = JSON.stringify(options.body);
    }
    const res = await fetch(url, { ...options, headers, credentials: 'same-origin' });
    const text = await res.text();
    let data = {};
    try {
        data = text ? JSON.parse(text) : {};
    } catch {
        data = {};
    }
    if (!res.ok) {
        const err = new Error(data.message || res.statusText || 'Request failed');
        err.status = res.status;
        err.data = data;
        throw err;
    }
    return data;
}

function isIos() {
    return /iPad|iPhone|iPod/.test(navigator.userAgent) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
}

function isStandalone() {
    return (
        window.matchMedia('(display-mode: standalone)').matches ||
        window.navigator.standalone === true
    );
}

function setStatus(root, message, variant = 'muted') {
    const el = root.querySelector('[data-push-status]');
    if (!el) {
        return;
    }
    el.textContent = message;
    el.classList.remove('text-muted-foreground', 'text-destructive', 'text-success');
    if (variant === 'destructive') {
        el.classList.add('text-destructive');
    } else if (variant === 'success') {
        el.classList.add('text-success');
    } else {
        el.classList.add('text-muted-foreground');
    }
}

export function initWebPushSettings() {
    const root = document.getElementById('pwa-push-settings');
    if (!root || root.dataset.pwaBound === '1') {
        return;
    }
    root.dataset.pwaBound = '1';

    const vapidUrl = root.dataset.vapidUrl;
    const subscribeUrl = root.dataset.subscribeUrl;
    const unsubscribeUrl = root.dataset.unsubscribeUrl;
    const testUrl = root.dataset.testUrl;

    const btnEnable = root.querySelector('[data-action="push-enable"]');
    const btnDisable = root.querySelector('[data-action="push-disable"]');
    const btnTest = root.querySelector('[data-action="push-test"]');

    if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
        setStatus(root, 'Push notifications are not supported in this browser.', 'muted');
        btnEnable?.setAttribute('disabled', 'disabled');
        btnDisable?.setAttribute('disabled', 'disabled');
        btnTest?.setAttribute('disabled', 'disabled');
        return;
    }

    if (isIos() && !isStandalone()) {
        setStatus(
            root,
            'On iPhone and iPad, add this app to your Home Screen first, then enable notifications.',
            'muted',
        );
    } else {
        setStatus(root, 'Enable notifications to get collab reminders on this device.', 'muted');
    }

    async function syncSubscriptionState() {
        const reg = await navigator.serviceWorker.ready;
        const sub = await reg.pushManager.getSubscription();
        if (sub) {
            btnEnable?.setAttribute('disabled', 'disabled');
            btnDisable?.removeAttribute('disabled');
            btnTest?.removeAttribute('disabled');
            setStatus(root, 'Notifications are enabled for this browser.', 'success');
        } else {
            btnEnable?.removeAttribute('disabled');
            btnDisable?.setAttribute('disabled', 'disabled');
            if (!isIos() || isStandalone()) {
                setStatus(root, 'Notifications are off for this browser.', 'muted');
            }
        }
    }

    syncSubscriptionState().catch(() => {});

    btnEnable?.addEventListener('click', async () => {
        try {
            if (Notification.permission === 'denied') {
                setStatus(root, 'Notifications are blocked. Enable them in browser settings.', 'destructive');
                return;
            }

            const reg =
                (await navigator.serviceWorker.getRegistration()) ||
                (await navigator.serviceWorker.register('/sw.js', { scope: '/' }));
            await reg.update();

            const { publicKey } = await fetchJson(vapidUrl);
            const key = urlBase64ToUint8Array(publicKey);

            let permission = Notification.permission;
            if (permission === 'default') {
                permission = await Notification.requestPermission();
            }
            if (permission !== 'granted') {
                setStatus(root, 'Permission was not granted.', 'destructive');
                return;
            }

            const subscription = await reg.pushManager.subscribe({
                userVisibleOnly: true,
                applicationServerKey: key,
            });

            const json = subscription.toJSON();
            await fetchJson(subscribeUrl, {
                method: 'POST',
                body: {
                    endpoint: json.endpoint,
                    keys: json.keys,
                    contentEncoding: 'aes128gcm',
                },
            });

            setStatus(root, 'Notifications enabled.', 'success');
            await syncSubscriptionState();
        } catch (e) {
            const msg = e?.data?.message ?? e?.message ?? 'Could not enable notifications.';
            setStatus(root, msg, 'destructive');
        }
    });

    btnDisable?.addEventListener('click', async () => {
        try {
            const reg = await navigator.serviceWorker.ready;
            const sub = await reg.pushManager.getSubscription();
            if (sub) {
                const json = sub.toJSON();
                await fetchJson(unsubscribeUrl, {
                    method: 'POST',
                    body: { endpoint: json.endpoint },
                });
                await sub.unsubscribe();
            }
            setStatus(root, 'Notifications disabled on this device.', 'muted');
            await syncSubscriptionState();
        } catch (e) {
            const msg = e?.data?.message ?? e?.message ?? 'Could not disable notifications.';
            setStatus(root, msg, 'destructive');
        }
    });

    btnTest?.addEventListener('click', async () => {
        try {
            await fetchJson(testUrl, { method: 'POST', body: {} });
            setStatus(root, 'Test notification queued. It may take a few seconds.', 'success');
        } catch (e) {
            const msg = e?.data?.message ?? e?.message ?? 'Could not queue test.';
            setStatus(root, msg, 'destructive');
        }
    });
}
