const CACHE_VERSION = 'ccw-pwa-v5';
const CORE_CACHE = `${CACHE_VERSION}-core`;
const RUNTIME_CACHE = `${CACHE_VERSION}-runtime`;

function scopePath() {
    try {
        return new URL(self.registration.scope).pathname.replace(/\/$/, '');
    } catch (error) {
        return '';
    }
}

function scoped(relativePath) {
    const base = scopePath();
    const normalized = String(relativePath || '').replace(/^\/+/, '');
    return `${base}/${normalized}`.replace(/\/{2,}/g, '/');
}

function coreAssets() {
    return [
        scoped('offline.html'),
        scoped('pages/login.php'),
        scoped('manifest.webmanifest'),
        scoped('assets/app.css'),
        scoped('assets/pwa.js'),
        scoped('assets/icons/favicon.svg'),
        scoped('assets/icons/apple-touch-icon.png'),
        scoped('assets/icons/icon-192.png'),
        scoped('assets/icons/icon-512.png'),
        scoped('assets/icons/icon-maskable-512.png'),
        scoped('assets/screenshots/install-wide.png'),
        scoped('assets/screenshots/install-mobile.png'),
    ];
}

self.addEventListener('install', (event) => {
    event.waitUntil(
        caches.open(CORE_CACHE).then((cache) => cache.addAll(coreAssets()))
    );
});

self.addEventListener('activate', (event) => {
    event.waitUntil(
        caches.keys().then((keys) =>
            Promise.all(
                keys.map((key) => {
                    if (key !== CORE_CACHE && key !== RUNTIME_CACHE) {
                        return caches.delete(key);
                    }

                    return Promise.resolve(false);
                })
            )
        ).then(() => self.clients.claim())
    );
});

async function networkFirst(request) {
    const runtimeCache = await caches.open(RUNTIME_CACHE);

    try {
        const response = await fetch(request);
        if (response && response.ok) {
            runtimeCache.put(request, response.clone());
        }

        return response;
    } catch (error) {
        const cachedResponse = await caches.match(request);
        if (cachedResponse) {
            return cachedResponse;
        }

        return caches.match(scoped('offline.html'));
    }
}

async function staleWhileRevalidate(request) {
    const runtimeCache = await caches.open(RUNTIME_CACHE);
    const cachedResponse = await caches.match(request);
    const fetchPromise = fetch(request)
        .then((response) => {
            if (response && response.ok) {
                runtimeCache.put(request, response.clone());
            }

            return response;
        })
        .catch(() => null);

    return cachedResponse || fetchPromise;
}

self.addEventListener('fetch', (event) => {
    const request = event.request;
    const requestUrl = new URL(request.url);

    if (request.method !== 'GET' || requestUrl.origin !== self.location.origin) {
        return;
    }

    if (request.mode === 'navigate') {
        event.respondWith(networkFirst(request));
        return;
    }

    const destination = request.destination;
    if (['style', 'script', 'image', 'font'].includes(destination) || requestUrl.pathname.endsWith('manifest.webmanifest')) {
        event.respondWith(staleWhileRevalidate(request));
    }
});

self.addEventListener('message', (event) => {
    if (event.data && event.data.type === 'SKIP_WAITING') {
        self.skipWaiting();
    }
});
