/* Service Worker for Church Music Management System */

const CACHE_NAME = "church-music-v1";
const API_CACHE_NAME = "church-music-api-v1";

// Static assets to cache immediately
const STATIC_ASSETS = [
  "/",
  "/index.html",
  "/static/css/main.css",
  "/static/js/main.js",
  "/favicon.ico",
  "/manifest.json",
];

// API endpoints to cache with time-based expiration
const API_CACHE_DURATION = 3 * 60 * 1000; // 3 minutes

// Install event - cache static assets
self.addEventListener("install", (event) => {
  console.log("[Service Worker] Installing...");

  event.waitUntil(
    caches
      .open(CACHE_NAME)
      .then((cache) => {
        console.log("[Service Worker] Caching static assets");
        // Don't fail if some assets are missing
        return Promise.allSettled(
          STATIC_ASSETS.map((url) =>
            cache
              .add(url)
              .catch((err) =>
                console.log(`[Service Worker] Failed to cache ${url}:`, err)
              )
          )
        );
      })
      .then(() => self.skipWaiting()) // Activate immediately
  );
});

// Activate event - clean up old caches
self.addEventListener("activate", (event) => {
  console.log("[Service Worker] Activating...");

  event.waitUntil(
    caches
      .keys()
      .then((cacheNames) => {
        return Promise.all(
          cacheNames
            .filter((name) => name !== CACHE_NAME && name !== API_CACHE_NAME)
            .map((name) => {
              console.log("[Service Worker] Deleting old cache:", name);
              return caches.delete(name);
            })
        );
      })
      .then(() => self.clients.claim()) // Take control immediately
  );
});

// Fetch event - serve from cache with network fallback
self.addEventListener("fetch", (event) => {
  const { request } = event;
  const url = new URL(request.url);

  // Skip cross-origin requests
  if (url.origin !== location.origin) {
    return;
  }

  // Handle API requests separately
  if (url.pathname.startsWith("/api/")) {
    event.respondWith(handleApiRequest(request));
    return;
  }

  // Handle static assets with cache-first strategy
  event.respondWith(
    caches.match(request).then((cachedResponse) => {
      if (cachedResponse) {
        console.log("[Service Worker] Serving from cache:", request.url);
        return cachedResponse;
      }

      // Not in cache, fetch from network
      return fetch(request)
        .then((response) => {
          // Cache successful responses
          if (response && response.status === 200) {
            const responseClone = response.clone();
            caches.open(CACHE_NAME).then((cache) => {
              cache.put(request, responseClone);
            });
          }
          return response;
        })
        .catch((error) => {
          console.error("[Service Worker] Fetch failed:", error);
          // Return offline page if available
          return caches.match("/offline.html").catch(() => {
            return new Response("Offline - Please check your connection", {
              status: 503,
              statusText: "Service Unavailable",
              headers: new Headers({
                "Content-Type": "text/plain",
              }),
            });
          });
        });
    })
  );
});

// Handle API requests with network-first, cache-fallback strategy
async function handleApiRequest(request) {
  const url = new URL(request.url);

  // Only cache GET requests
  if (request.method !== "GET") {
    return fetch(request);
  }

  try {
    // Try network first
    const response = await fetch(request);

    if (response && response.status === 200) {
      // Clone and cache the response with timestamp
      const responseClone = response.clone();
      const cache = await caches.open(API_CACHE_NAME);

      // Add timestamp header for expiration
      const cachedResponse = new Response(await responseClone.blob(), {
        status: responseClone.status,
        statusText: responseClone.statusText,
        headers: {
          ...Object.fromEntries(responseClone.headers.entries()),
          "sw-cached-at": Date.now().toString(),
        },
      });

      await cache.put(request, cachedResponse);
      console.log("[Service Worker] Cached API response:", url.pathname);
    }

    return response;
  } catch (error) {
    console.log("[Service Worker] Network failed, trying cache:", url.pathname);

    // Network failed, try cache
    const cachedResponse = await caches.match(request);

    if (cachedResponse) {
      // Check if cache is still fresh
      const cachedAt = cachedResponse.headers.get("sw-cached-at");
      const now = Date.now();

      if (cachedAt && now - parseInt(cachedAt) < API_CACHE_DURATION) {
        console.log("[Service Worker] Serving fresh cached API response");
        return cachedResponse;
      } else {
        console.log("[Service Worker] Cached API response expired");
        // Return stale cache with warning header
        return new Response(await cachedResponse.blob(), {
          status: cachedResponse.status,
          statusText: cachedResponse.statusText,
          headers: {
            ...Object.fromEntries(cachedResponse.headers.entries()),
            "sw-cache-status": "stale",
          },
        });
      }
    }

    // No cache available
    throw error;
  }
}

// Listen for messages from the client
self.addEventListener("message", (event) => {
  if (event.data && event.data.type === "SKIP_WAITING") {
    self.skipWaiting();
  }

  if (event.data && event.data.type === "CLEAR_CACHE") {
    caches
      .keys()
      .then((cacheNames) => {
        return Promise.all(cacheNames.map((name) => caches.delete(name)));
      })
      .then(() => {
        event.ports[0].postMessage({ success: true });
      });
  }
});
