<?php
/**
 * EduTrack Parent Portal Service Worker
 */
header('Content-Type: application/javascript; charset=utf-8');
header('Cache-Control: no-cache, no-store, must-revalidate');

require_once __DIR__ . '/../config.php';

$basePath   = rtrim((string) parse_url(rtrim(BASE_URL, '/'), PHP_URL_PATH), '/');
$portalPath = $basePath . '/parent-portal';
$ver        = '1.0.3';
?>
/* EduTrack Parent Portal Service Worker — v<?= $ver ?> */
'use strict';

const CACHE      = 'edutrack-parent-v<?= $ver ?>';
const PORTAL     = '<?= $portalPath ?>';   // e.g. '/edutrack/parent-portal'
const OFFLINE    = PORTAL + '/offline.html';

const PRECACHE = [
  OFFLINE,
  PORTAL + '/assets/css/portal.css',
];

// ── Install ───────────────────────────────────────────────────────────────
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE).then(cache =>
      cache.addAll(PRECACHE.map(url => new Request(url, { cache: 'reload' })))
    ).catch(err => console.warn('[EduTrack Parent SW] Precache failed:', err))
  );
  self.skipWaiting();
});

// ── Activate ──────────────────────────────────────────────────────────────
self.addEventListener('activate', event => {
  event.waitUntil(
    caches.keys().then(keys =>
      Promise.all(
        keys.filter(k => k.startsWith('edutrack-parent-') && k !== CACHE)
            .map(k => caches.delete(k))
      )
    )
  );
  self.clients.claim();
});

// ── Fetch ─────────────────────────────────────────────────────────────────
self.addEventListener('fetch', event => {
  const req = event.request;
  const url = new URL(req.url);

  if (req.method !== 'GET') return;

  // CDN — Stale While Revalidate
  if (
    url.hostname.includes('jsdelivr.net') ||
    url.hostname.includes('cloudflare.com') ||
    url.hostname.includes('fonts.googleapis.com') ||
    url.hostname.includes('fonts.gstatic.com') ||
    url.hostname.includes('code.jquery.com')
  ) {
    event.respondWith(staleWhileRevalidate(req));
    return;
  }

  if (url.origin !== self.location.origin) return;
  if (!url.pathname.startsWith(PORTAL)) return;

  // Static assets — Cache First
  if (/\.(css|js|png|jpg|jpeg|gif|svg|webp|ico|woff|woff2|ttf|eot)(\?.*)?$/i.test(url.pathname)) {
    event.respondWith(cacheFirst(req));
    return;
  }

  // Ajax — Network First with JSON fallback
  if (url.pathname.includes('/ajax/')) {
    event.respondWith(networkFirstAjax(req));
    return;
  }

  // Pages — Network First with offline fallback
  event.respondWith(networkFirstPage(req));
});

async function cacheFirst(req) {
  const cached = await caches.match(req);
  if (cached) return cached;
  try {
    const res = await fetch(req);
    if (res.ok) {
      const cache = await caches.open(CACHE);
      cache.put(req, res.clone());
    }
    return res;
  } catch {
    return new Response('Asset unavailable offline.', { status: 503 });
  }
}

async function networkFirstPage(req) {
  try {
    const res = await fetch(req);
    if (res.ok || res.type === 'opaqueredirect') {
      const cache = await caches.open(CACHE);
      cache.put(req, res.clone());
    }
    return res;
  } catch {
    const cached = await caches.match(req);
    if (cached) return cached;
    const offline = await caches.match(OFFLINE);
    return offline || new Response('<h1>Offline</h1>', {
      status: 503,
      headers: { 'Content-Type': 'text/html' },
    });
  }
}

async function networkFirstAjax(req) {
  try {
    const res = await fetch(req);
    if (res.ok) {
      const isMutation = /action=(save|delete|update|insert|remove|reset|upload|send)/i.test(req.url);
      if (!isMutation) {
        const cache = await caches.open(CACHE);
        cache.put(req, res.clone());
      }
    }
    return res;
  } catch {
    const cached = await caches.match(req);
    if (cached) return cached;
    return new Response(
      JSON.stringify({ success: false, offline: true, message: 'You are offline. Please reconnect to continue.' }),
      { status: 503, headers: { 'Content-Type': 'application/json' } }
    );
  }
}

async function staleWhileRevalidate(req) {
  const cache  = await caches.open(CACHE);
  const cached = await cache.match(req);
  const networkFetch = fetch(req)
    .then(res => { if (res.ok) cache.put(req, res.clone()); return res; })
    .catch(() => null);
  return cached || (await networkFetch) || new Response('', { status: 503 });
}
