// 🚀 ENTERPRISE SERVICE WORKER - FleekDash V2
// Advanced caching strategies for optimal performance

const CACHE_VERSION = "enterprise-v4";
const STATIC_CACHE = `fleekdash-static-${CACHE_VERSION}`;
const DYNAMIC_CACHE = `fleekdash-dynamic-${CACHE_VERSION}`;
const API_CACHE = `fleekdash-api-${CACHE_VERSION}`;
const FONT_CACHE = `fleekdash-fonts-${CACHE_VERSION}`;
const IMAGE_CACHE = `fleekdash-images-${CACHE_VERSION}`;

// 🎯 ENTERPRISE OPTIMIZATION: Cache strategies configuration
const CACHE_STRATEGIES = {
  // Static assets: Cache First with long TTL
  static: { strategy: "cacheFirst", ttl: 3600000 }, // 1 hour

  // API endpoints: Network First with short TTL
  api: { strategy: "networkFirst", ttl: 300000 }, // 5 minutes

  // Charts and heavy components: Stale While Revalidate
  charts: { strategy: "staleWhileRevalidate", ttl: 1800000 }, // 30 minutes

  // Fonts: Cache First with very long TTL
  fonts: { strategy: "cacheFirst", ttl: 86400000 }, // 24 hours

  // Images: Cache First with medium TTL
  images: { strategy: "cacheFirst", ttl: 7200000 }, // 2 hours
};

// 🎯 ENTERPRISE OPTIMIZATION: Performance monitoring
const PERFORMANCE_METRICS = {
  cacheHits: 0,
  cacheMisses: 0,
  networkRequests: 0,
  averageResponseTime: 0,
};

// 🎯 ENTERPRISE OPTIMIZATION: Rate limiting configuration
const RATE_LIMITING = {
  requestsPerMinute: 300, // Increased from 30 to 300 for search-as-you-type
  burstLimit: 20, // Allow larger bursts for fast typing
  cooldownPeriod: 10000, // Reduced to 10s cooldown
  retryDelay: 500, // 0.5s base delay
  maxRetries: 2, // Maximum retry attempts
};

// Rate limiting state
const RATE_LIMIT_STATE = {
  requestQueue: [],
  lastRequestTime: 0,
  requestCount: 0,
  isCoolingDown: false,
  cooldownEndTime: 0,
};

// Dynamic asset detection using manifest
let STATIC_ASSETS = [];

// Helper function to safely parse JSON responses
async function safeJsonParse(response, context = "unknown") {
  // Validate response status
  if (!response.ok) {
    console.warn(
      `[SW] ${context}: Request failed with status ${response.status}`,
    );
    throw new Error(`Request failed: ${response.status}`);
  }

  // Check if response is actually JSON
  const contentType = response.headers.get("content-type");
  if (!contentType?.includes("application/json")) {
    console.warn(
      `[SW] ${context}: Non-JSON content type received: ${
        contentType || "none"
      }`,
    );
    // Try to get response text for debugging
    try {
      const text = await response.text();
      const preview = text.substring(0, 100);
      console.warn(`[SW] ${context}: Response preview:`, preview);
    } catch (error) {
      console.warn(`[SW] ${context}: Failed to read response text preview`, error);
    }
    throw new Error(`Invalid content type: ${contentType || "none"}`);
  }

  // Parse JSON
  try {
    return await response.json();
  } catch (error) {
    console.error(`[SW] ${context}: JSON parse error:`, error);
    throw new Error(`JSON parse failed: ${error.message}`);
  }
}

// Function to load manifest and extract assets
async function loadManifestAssets() {
  try {
    const response = await fetch(
      "/wp-content/plugins/fleekdash/assets/manifest.json",
    );
    const manifest = await safeJsonParse(response, "loadManifestAssets");

    // Extract core assets from manifest
    const coreAssets = [
      "/wp-content/plugins/fleekdash/assets/core.css",
      "/wp-content/plugins/fleekdash/assets/index.css",
    ];
    if (manifest["src/styles/globals.css"]) {
      coreAssets.push(
        "/wp-content/plugins/fleekdash/assets/" +
          manifest["src/styles/globals.css"].file,
      );
    }

    // Core JS files (always available)
    if (manifest["src/admin/entry.tsx"]) {
      coreAssets.push(
        "/wp-content/plugins/fleekdash/assets/" +
          manifest["src/admin/entry.tsx"].file,
      );
    }
    if (manifest["src/admin/react-preload.ts"]) {
      coreAssets.push(
        "/wp-content/plugins/fleekdash/assets/" +
          manifest["src/admin/react-preload.ts"].file,
      );
    }

    // Main bundle (if available)
    if (manifest["src/admin/pages/dashboard/index.tsx"]) {
      coreAssets.push(
        "/wp-content/plugins/fleekdash/assets/" +
          manifest["src/admin/pages/dashboard/index.tsx"].file,
      );
    }

    // CMDK and Search assets
    const cmdkAssets = [];
    if (manifest["src/admin/pages/settings/search-settings.tsx"]) {
      cmdkAssets.push(
        "/wp-content/plugins/fleekdash/assets/" +
          manifest["src/admin/pages/settings/search-settings.tsx"].file,
      );
    }

    STATIC_ASSETS = coreAssets;
    CMDK_ASSETS = cmdkAssets;
    return coreAssets;
  } catch (error) {
    console.warn("[SW] Failed to load manifest, using fallback assets:", error);
    // Fallback to basic assets
    STATIC_ASSETS = [
      "/wp-content/plugins/fleekdash/assets/core.css",
      "/wp-content/plugins/fleekdash/assets/index.css",
    ];
    CMDK_ASSETS = []; // Empty fallback for CMDK
    return STATIC_ASSETS;
  }
}

// Fonts to cache
const FONT_ASSETS = [
  "/wp-content/plugins/fleekdash/public/fonts/Inter-Regular.woff",
  "/wp-content/plugins/fleekdash/public/fonts/Inter-Medium.woff",
  "/wp-content/plugins/fleekdash/public/fonts/Inter-SemiBold.woff",
];

// CMDK and Search assets to cache (will be populated dynamically)
let CMDK_ASSETS = [];

// Assets to cache on demand
const CACHE_ON_DEMAND = [
  "/wp-content/plugins/fleekdash/assets/admin/vendor-charts/",
  "/wp-content/plugins/fleekdash/assets/admin/vendor-grid/",
  "/wp-content/plugins/fleekdash/assets/admin/vendor-motion/",
];

// Install event - simplified to avoid installation failures
globalThis.addEventListener("install", (event) => {
  event.waitUntil(
    // Simple installation - just skip waiting
    Promise.resolve()
      .then(() => {
        return globalThis.skipWaiting(); // Activate immediately
      })
      .catch((error) => {
        console.error("[SW] Installation failed:", error);
        // Still skip waiting even if something fails
        return globalThis.skipWaiting();
      }),
  );
});

// Activate event - cache assets after installation
globalThis.addEventListener("activate", (event) => {
  event.waitUntil(
    Promise.all([
      // Take control of all clients immediately
      globalThis.clients.claim(),

      // Clean old caches
      cleanOldCaches(),

      // Cache assets in background (non-blocking)
      cacheAssetsInBackground(),
    ]    )
      .then(() => {
        return globalThis.clients.matchAll().then((clients) => {
          clients.forEach((client) => {
            try {
              client.postMessage({ type: "SW_READY" });
            } catch (error) {
              console.warn("[SW] Failed to notify client:", error);
            }
          });
        });
      })
      .catch((error) => {
        console.error("[SW] Activation failed:", error);
      }),
  );
});

// Listen for cache clear messages from the app (e.g., after applying a theme)
function getMessageEventOrigin(event) {
  if (typeof event.origin === "string" && event.origin.length > 0) {
    return event.origin;
  }

  const sourceUrl = event.source?.url;
  if (typeof sourceUrl === "string" && sourceUrl.length > 0) {
    try {
      return new URL(sourceUrl).origin;
    } catch (error) {
      console.warn("[SW] Failed to parse event source URL origin:", error);
    }
  }

  return "";
}

function isTrustedMessageEvent(event) {
  const messageOrigin = getMessageEventOrigin(event);
  return messageOrigin !== "" && messageOrigin === globalThis.location.origin;
}

globalThis.addEventListener("message", (event) => { // NOSONAR: origin is validated by isTrustedMessageEvent(event)
  if (!isTrustedMessageEvent(event)) {
    console.warn("[SW] Ignored untrusted message event");
    return;
  }

  const data = event?.data || {};
  if (!data || typeof data !== "object") return;

  if (data.type === "FLEEKDASH_CLEAR_THEME_CACHE") {
    const targets = Array.isArray(data.urls) ? data.urls : [];
    event.waitUntil(
      (async () => {
        try {
          const cache = await caches.open(DYNAMIC_CACHE);
          const keys = await cache.keys();
          await Promise.all(
            keys.map((request) => {
              const url = new URL(request.url);
              const shouldDelete = targets.some((t) => {
                try {
                  // Allow relative match or absolute match
                  return url.pathname.includes(t);
                } catch {
                  return false;
                }
              });
              if (shouldDelete) {
                return cache.delete(request);
              }
              return Promise.resolve(false);
            }),
          );
        } catch (err) {
          console.warn("[SW] Failed clearing theme caches:", err);
        }
      })(),
    );
  }
});

// Background asset caching (non-blocking)
async function cacheAssetsInBackground() {
  try {
    const assets = await loadManifestAssets();
    const cache = await caches.open(STATIC_CACHE);

    await Promise.allSettled(
      assets.map(async (asset) => {
        try {
          await cache.add(asset);
          return { success: true, asset };
        } catch (error) {
          console.warn("[SW] Failed to cache:", asset, error.message);
          return { success: false, asset, error: error.message };
        }
      }),
    );

    // Cache fonts
    const fontCache = await caches.open(FONT_CACHE);
    await Promise.allSettled(
      FONT_ASSETS.map(async (asset) => {
        try {
          await fontCache.add(asset);
          return { success: true, asset };
        } catch (error) {
          console.warn("[SW] Failed to cache font:", asset, error.message);
          return { success: false, asset, error: error.message };
        }
      }),
    );

    await Promise.allSettled(
      CMDK_ASSETS.map(async (asset) => {
        try {
          await cache.add(asset);
          return { success: true, asset };
        } catch (error) {
          console.warn(
            "[SW] Failed to cache CMDK asset:",
            asset,
            error.message,
          );
          return { success: false, asset, error: error.message };
        }
      }),
    );

    // Chart preload is NOT run here - only when the app sends PRELOAD_CHARTS (e.g. on dashboard).
    // This avoids 404s and unnecessary work on pages that don't use charts (e.g. Media).

    // Preload critical endpoints
    try {
      await preloadEndpoints();
    } catch (error) {
      console.warn("[SW] Failed to preload endpoints:", error);
    }
  } catch (error) {
    console.error("[SW] Background caching failed:", error);
  }
}

// Clean old caches function (called from activate event)
async function cleanOldCaches() {
  try {
    const cacheNames = await caches.keys();
    const validCaches = new Set([STATIC_CACHE, DYNAMIC_CACHE, FONT_CACHE]);

    const deletePromises = cacheNames.map((cacheName) => {
      if (!validCaches.has(cacheName) && cacheName.startsWith("fleekdash-")) {
        return caches.delete(cacheName);
      }
    });

    await Promise.all(deletePromises);
  } catch (error) {
    console.error("[SW] Failed to clean old caches:", error);
  }
}

// Fetch event - implement caching strategies
globalThis.addEventListener("fetch", (event) => {
  const { request } = event;
  const url = new URL(request.url);

  // Skip non-GET requests
  if (request.method !== "GET") return;

  // Skip non-FleekDash REST API calls only; allow assets
  if (
    url.pathname.startsWith("/wp-json/") &&
    !url.pathname.includes("/fleekdash/")
  ) {
    return;
  }

  event.respondWith(handleRequest(request));
});

function shouldBypassFleekdashApiCache(pathname) {
  return (
    pathname.includes("/wp-json/fleekdash/v1/roles") ||
    pathname.includes("/wp-json/fleekdash/v1/permissions") ||
    pathname.includes("/wp-json/fleekdash/v1/auth/check-capability") ||
    pathname.includes("/wp-json/fleekdash/v1/features/check") ||
    pathname.includes("/wp-json/fleekdash/v1/permission-requests") ||
    pathname.includes("/wp-json/fleekdash/v1/permissions/overrides") ||
    // Toolbar/menu are user-session specific (capabilities, plugins, counters).
    // Caching by URL causes cross-session stale payloads and inconsistent UI.
    pathname.includes("/wp-json/fleekdash/v1/toolbar") ||
    pathname.includes("/wp-json/fleekdash/v1/menu")
  );
}

async function handleRequest(request) {
  const url = new URL(request.url);

  try {
    // Font files - Cache First with long TTL
    if (
      url.pathname.includes("/wp-content/plugins/fleekdash/public/fonts/") ||
      request.destination === "font"
    ) {
      return await cacheFirst(request, FONT_CACHE, 86400000); // 24 hours
    }

    // Plugin static assets (JS, CSS) - Cache First
    if (
      url.pathname.startsWith("/wp-content/plugins/fleekdash/assets/") &&
      (url.pathname.endsWith(".js") || url.pathname.endsWith(".css"))
    ) {
      return await cacheFirst(request, STATIC_CACHE, 3600000); // 1 hour
    }

    // CMDK and Search assets - Cache First for instant loading
    if (
      url.pathname.includes("search-settings") ||
      url.pathname.includes("cmdk") ||
      url.pathname.includes("command")
    ) {
      return await cacheFirst(request, STATIC_CACHE, 3600000); // 1 hour
    }

    // Charts and heavy components - Network First with fallback
    if (
      url.pathname.includes("vendor-charts") ||
      url.pathname.includes("vendor-grid") ||
      url.pathname.includes("nivo-overview") ||
      url.pathname.includes("BarChart") ||
      url.pathname.includes("ChartWidget") ||
      url.pathname.includes("RechartsOverview")
    ) {
      return await networkFirst(request, DYNAMIC_CACHE, 1800000); // 30 minutes
    }

    // API requests - Network First with short cache
    if (url.pathname.includes("/wp-json/fleekdash/")) {
      // Never cache permission-sensitive or role/override endpoints.
      if (
        shouldBypassFleekdashApiCache(url.pathname) ||
        url.pathname.includes("/wp-json/fleekdash/v1/features") ||
        url.pathname.includes("/wp-json/fleekdash/v1/settings/labs")
      ) {
        return await fetch(request, { cache: "no-store" });
      }
      return await networkFirst(request, DYNAMIC_CACHE, 300000); // 5 minutes
    }

    // Default: Network First
    return await networkFirst(request, DYNAMIC_CACHE, 600000); // 10 minutes
  } catch (error) {
    if (error.name === "AbortError") {
      return new Response(
        JSON.stringify({ error: "Request aborted", code: 499 }),
        {
          status: 499,
          headers: { "Content-Type": "application/json" },
        },
      );
    }
    console.error("[SW] Request failed:", error);
    return new Response(JSON.stringify({ error: "Network error", code: 503 }), {
      status: 503,
      headers: { "Content-Type": "application/json" },
    });
  }
}

// Cache First strategy with TTL
async function cacheFirst(request, cacheName, ttl) {
  const cache = await caches.open(cacheName);
  const cached = await cache.match(request);

  if (cached) {
    const cachedDate = new Date(cached.headers.get("sw-cached-date"));
    const now = new Date();

    // Check if cache is still valid
    if (now - cachedDate < ttl) {
      return cached;
    }
  }

  // Fetch from network
  try {
    const response = await fetch(request);
    if (response.ok) {
      const responseClone = response.clone();
      const headers = new Headers(responseClone.headers);
      headers.set("sw-cached-date", new Date().toISOString());

      const modifiedResponse = new Response(responseClone.body, {
        status: responseClone.status,
        statusText: responseClone.statusText,
        headers: headers,
      });

      cache.put(request, modifiedResponse);
    }
    return response;
  } catch (error) {
    // Check if the request was aborted
    if (error.name === "AbortError") {
      // Return cached version if available, even if expired
      if (cached) {
        return cached;
      }

      // If user cancelled, return 499
      if (request.signal?.aborted) {
        return new Response(
          JSON.stringify({ error: "Request cancelled", code: 499 }),
          {
            status: 499,
            headers: { "Content-Type": "application/json" },
          },
        );
      }

      // Return a proper JSON error response for aborted requests
      return new Response(
        JSON.stringify({ error: "Request timeout", code: 408 }),
        {
          status: 408,
          headers: { "Content-Type": "application/json" },
        },
      );
    }

    // Return cached version if available, even if expired
    if (cached) {
      return cached;
    }
    // Return a proper JSON error response instead of throwing
    return new Response(JSON.stringify({ error: "Network error", code: 503 }), {
      status: 503,
      headers: { "Content-Type": "application/json" },
    });
  }
}

// Network First strategy with TTL and rate limiting for API requests
async function networkFirst(request, cacheName, ttl) {
  const cache = await caches.open(cacheName);
  const url = new URL(request.url);
  const isApiRequest = isRateLimitedApiRequest(url);

  try {
    const response = isApiRequest
      ? await fetchRateLimitedApiRequest(request)
      : await fetchWithTimeoutSignal(request, 5000);

    if (response.ok) {
      const responseClone = response.clone();
      const headers = new Headers(responseClone.headers);
      headers.set("sw-cached-date", new Date().toISOString());

      const modifiedResponse = new Response(responseClone.body, {
        status: responseClone.status,
        statusText: responseClone.statusText,
        headers: headers,
      });

      cache.put(request, modifiedResponse);
    }

    return response;
  } catch (error) {
    return handleNetworkFirstError(error, request, cache);
  }
}

function isRateLimitedApiRequest(url) {
  return (
    url.pathname.includes("/wp-json/fleekdash/") ||
    url.pathname.includes("/api/") ||
    url.hostname.includes("fleekdash")
  );
}

function createJsonErrorResponse(body, status) {
  return new Response(JSON.stringify(body), {
    status,
    headers: { "Content-Type": "application/json" },
  });
}

async function fetchRateLimitedApiRequest(request) {
  return rateLimitedFetch(request.url, {
    method: request.method,
    headers: request.headers,
    signal: request.signal,
  });
}

async function fetchWithTimeoutSignal(request, timeoutMs) {
  const timeoutController = new AbortController();
  const timeoutId = setTimeout(() => timeoutController.abort(), timeoutMs);
  let combinedSignal = timeoutController.signal;

  if (request.signal && "any" in AbortSignal) {
    combinedSignal = AbortSignal.any([request.signal, timeoutController.signal]);
  }

  try {
    return await fetch(request, { signal: combinedSignal });
  } finally {
    clearTimeout(timeoutId);
  }
}

async function findCachedResponseAcrossAllCaches(request) {
  const cacheNames = await caches.keys();
  for (const cacheName of cacheNames) {
    const currentCache = await caches.open(cacheName);
    const cached = await currentCache.match(request);
    if (cached) {
      return cached;
    }
  }
  return null;
}

async function handleNetworkFirstError(error, request, primaryCache) {
  if (error.message === "Rate limit exceeded") {
    const cached = await primaryCache.match(request);
    if (cached) {
      return cached;
    }
    return createJsonErrorResponse(
      {
        error: "Rate limit exceeded",
        code: 429,
        message: "Too many requests, please try again later",
      },
      429,
    );
  }

  if (error.name === "AbortError") {
    if (request.signal?.aborted) {
      return createJsonErrorResponse({ error: "Request cancelled", code: 499 }, 499);
    }

    const cached = await findCachedResponseAcrossAllCaches(request);
    if (cached) {
      return cached;
    }

    return createJsonErrorResponse({ error: "Request timeout", code: 408 }, 408);
  }

  const cached = await primaryCache.match(request);
  if (cached) {
    return cached;
  }

  return createJsonErrorResponse({ error: "Network error", code: 503 }, 503);
}

// Background sync for cache cleanup
globalThis.addEventListener("message", (event) => { // NOSONAR: origin is validated by isTrustedMessageEvent(event)
  if (!isTrustedMessageEvent(event)) {
    console.warn("[SW] Ignored untrusted cleanup message event");
    return;
  }

  if (event.data?.type === "CLEANUP_CACHE") {
    cleanupOldCaches();
  }
});

async function cleanupOldCaches() {
  // NOTE: do not shadow the global `caches` object
  const cacheNames = await caches.keys();
  const now = new Date();

  for (const cacheName of cacheNames) {
    if (cacheName.startsWith("fleekdash-")) {
      const cache = await caches.open(cacheName);
      const requests = await cache.keys();

      for (const request of requests) {
        const response = await cache.match(request);
        const cachedDate = new Date(response.headers.get("sw-cached-date"));

        // Remove entries older than 24 hours
        if (now - cachedDate > 86400000) {
          await cache.delete(request);
        }
      }
    }
  }
}

// Helper function to safely respond via MessagePort
function safePortResponse(port, data) {
  if (!port) return;

  try {
    // Check if port is still open before sending
    port.postMessage(data);
  } catch (error) {
    console.warn("[SW] MessagePort response skipped because port is unavailable:", error);
  }
}

// Message handling for cache management
globalThis.addEventListener("message", async (event) => { // NOSONAR: origin is validated by isTrustedMessageEvent(event)
  if (!isTrustedMessageEvent(event)) {
    console.warn("[SW] Ignored untrusted message command event");
    return;
  }

  if (!event.data || typeof event.data !== "object") {
    return;
  }

  const { type } = event.data;
  const port = event.ports && event.ports.length > 0 ? event.ports[0] : null;

  switch (type) {
    case "SKIP_WAITING":
      globalThis.skipWaiting();
      break;

    case "PING":
      safePortResponse(port, { success: true });
      break;

    case "CLEAR_CACHE":
      clearAllCaches()
        .then(() => {
          safePortResponse(port, { success: true });
        })
        .catch((error) => {
          console.error("[SW] Error clearing cache:", error);
          safePortResponse(port, { success: false, error: error.message });
        });
      break;

    case "PRELOAD_CHARTS":
      try {
        await preloadCharts();
        safePortResponse(port, { success: true });
      } catch (error) {
        console.error("[SW] Error preloading charts:", error);
        safePortResponse(port, { success: false, error: error.message });
      }
      break;

    case "PRELOAD_ENDPOINTS":
      try {
        await preloadEndpoints();
        safePortResponse(port, { success: true });
      } catch (error) {
        console.error("[SW] Error preloading endpoints:", error);
        safePortResponse(port, { success: false, error: error.message });
      }
      break;

    case "PRELOAD_CMDK":
      preloadCMDK()
        .then(() => {
          safePortResponse(port, { success: true });
        })
        .catch((error) => {
          console.error("[SW] Error preloading CMDK:", error);
          safePortResponse(port, { success: false, error: error.message });
        });
      break;

    default:
      break;
  }
});

// Clear all caches
async function clearAllCaches() {
  try {
    const cacheNames = await caches.keys();
    await Promise.all(
      cacheNames
        .filter((name) => name.startsWith("fleekdash-"))
        .map((name) => caches.delete(name)),
    );
  } catch (error) {
    console.error("[SW] Error clearing caches:", error);
  }
}

// Preload chart assets dynamically
async function preloadCharts() {
  try {
    const cache = await caches.open(DYNAMIC_CACHE);

    const response = await fetch(
      "/wp-content/plugins/fleekdash/assets/manifest.json",
    );
    // Tolerate 404/missing manifest (e.g. dev without build) - skip preload without throwing
    if (!response.ok) {
      if (response.status !== 404) {
        console.warn(
          "[SW] preloadCharts: Request failed with status",
          response.status,
        );
      }
      return;
    }
    const manifest = await safeJsonParse(response, "preloadCharts");

    const chartAssets = [];

    // Extract chart-related assets from manifest by name pattern (not hardcoded hash)
    Object.keys(manifest).forEach((key) => {
      const asset = manifest[key];
      if (asset.name) {
        // Look for chart-related components by name
        if (
          asset.name === "BarChart" ||
          asset.name === "ChartWidget" ||
          asset.name === "RechartsOverview" ||
          asset.name === "DashboardMainChart" ||
          asset.name === "DashboardCore" ||
          asset.name === "DashboardIsolationProvider" ||
          asset.name === "DraggableDashboard" ||
          asset.name === "stats-card-with-progress" ||
          asset.name === "usePostsStats" ||
          asset.name === "usePreloadCharts" ||
          asset.name.includes("Chart") ||
          asset.name.includes("Recharts") ||
          asset.name.includes("Dashboard") ||
          asset.name.includes("Stats")
        ) {
          chartAssets.push(
            "/wp-content/plugins/fleekdash/assets/" + asset.file,
          );
        }
      }
    });

    await Promise.allSettled(
      chartAssets.map(async (asset) => {
        try {
          await cache.add(asset);
          return { success: true, asset };
        } catch (error) {
          console.warn(
            "[SW] Failed to preload chart asset:",
            asset,
            error.message,
          );
          return { success: false, asset, error: error.message };
        }
      }),
    );
  } catch (error) {
    console.error("[SW] Error preloading charts:", error);
    throw error; // Re-throw to be caught by caller
  }
}

// Rate limiting functions
function checkRateLimit() {
  const now = Date.now();
  const timeWindow = 60000; // 1 minute

  // Reset counter if we're in a new time window
  if (now - RATE_LIMIT_STATE.lastRequestTime > timeWindow) {
    RATE_LIMIT_STATE.requestCount = 0;
    RATE_LIMIT_STATE.lastRequestTime = now;
  }

  // Check if we're over the rate limit
  if (RATE_LIMIT_STATE.requestCount >= RATE_LIMITING.requestsPerMinute) {
    if (!RATE_LIMIT_STATE.isCoolingDown) {
      RATE_LIMIT_STATE.isCoolingDown = true;
      RATE_LIMIT_STATE.cooldownEndTime = now + RATE_LIMITING.cooldownPeriod;
    }
    return false;
  }

  return true;
}

function recordRequest() {
  RATE_LIMIT_STATE.requestCount++;
}

async function waitForRateLimit() {
  const now = Date.now();

  if (RATE_LIMIT_STATE.isCoolingDown) {
    const waitTime = RATE_LIMIT_STATE.cooldownEndTime - now;
    if (waitTime > 0) {
      await new Promise((resolve) => setTimeout(resolve, waitTime));
    }
    // Reset cooldown state
    RATE_LIMIT_STATE.isCoolingDown = false;
    RATE_LIMIT_STATE.requestCount = 0;
  }
}

async function rateLimitedFetch(url, options = {}, retryCount = 0) {
  // Wait for rate limit availability
  await waitForRateLimit();

  // Check rate limit before making request
  if (!checkRateLimit()) {
    throw new Error("Rate limit exceeded");
  }

  try {
    recordRequest();
    const response = await fetch(url, options);

    // If we get a 429 (Too Many Requests), implement exponential backoff
    if (response.status === 429 && retryCount < RATE_LIMITING.maxRetries) {
      const delay = RATE_LIMITING.retryDelay * Math.pow(2, retryCount);
      await new Promise((resolve) => setTimeout(resolve, delay));
      return rateLimitedFetch(url, options, retryCount + 1);
    }

    return response;
  } catch (error) {
    if (retryCount < RATE_LIMITING.maxRetries) {
      const delay = RATE_LIMITING.retryDelay * Math.pow(2, retryCount);
      await new Promise((resolve) => setTimeout(resolve, delay));
      return rateLimitedFetch(url, options, retryCount + 1);
    }
    throw error;
  }
}

// Preload critical API endpoints for instant dashboard loading
async function preloadEndpoints() {
  try {
    const cache = await caches.open(DYNAMIC_CACHE);

    // Critical dashboard endpoints to preload (reduced to prevent 429 errors)
    // Updated for V2.2 endpoints
    const baseUrl = globalThis.location.origin;
    const endpoints = [
      `${baseUrl}/wp-json/fleekdash/v1/posts`,
      `${baseUrl}/wp-json/fleekdash/v1/comments`,
      `${baseUrl}/wp-json/fleekdash/v1/analytics/visitors`,
      `${baseUrl}/wp-json/fleekdash/v1/analytics/kpi-summary`,
      `${baseUrl}/wp-json/fleekdash/v1/growth-meter/analytics`,
      // ENTERPRISE UNIFIED DASHBOARD - only preload essential time ranges
      `${baseUrl}/wp-json/fleekdash/v1/dashboard/unified?time_range=7d`,
      `${baseUrl}/wp-json/fleekdash/v1/dashboard/unified?time_range=30d`,
      `${baseUrl}/wp-json/fleekdash/v1/dashboard/performance`,
    ];

    // Process endpoints sequentially with rate limiting to prevent 429 errors
    for (const endpoint of endpoints) {
      try {
        const response = await rateLimitedFetch(endpoint, {
          method: "GET",
          headers: {
            "Content-Type": "application/json",
          },
        });

        if (response.ok) {
          const responseClone = response.clone();
          await cache.put(endpoint, responseClone);
        } else {
          console.warn(
            "[SW] Endpoint returned non-OK status:",
            endpoint,
            response.status,
          );
        }
      } catch (error) {
        console.warn(
          "[SW] Failed to preload endpoint:",
          endpoint,
          error.message,
        );
      }

      // Small delay between requests to be respectful to the server
      await new Promise((resolve) => setTimeout(resolve, 200));
    }
  } catch (error) {
    console.error("[SW] Error preloading endpoints:", error);
    throw error;
  }
}

// Preload CMDK assets for instant command menu
async function preloadCMDK() {
  try {
    const cache = await caches.open(STATIC_CACHE);

    // Use the dynamically loaded CMDK assets
    const cmdkAssets = CMDK_ASSETS.length > 0 ? CMDK_ASSETS : [];

    // If no CMDK assets loaded yet, try to load them dynamically
    if (cmdkAssets.length === 0) {
      try {
        const response = await fetch(
          "/wp-content/plugins/fleekdash/assets/manifest.json",
        );
        const manifest = await safeJsonParse(response, "preloadCMDK");

        // Extract CMDK-related assets from manifest by name pattern (not hardcoded hash)
        Object.keys(manifest).forEach((key) => {
          const asset = manifest[key];
          if (asset.name) {
            // Look for CMDK/search-related components by name
            if (
              asset.name.includes("search") ||
              asset.name.includes("Search") ||
              asset.name.includes("CMDK") ||
              asset.name.includes("cmdk")
            ) {
              cmdkAssets.push(
                "/wp-content/plugins/fleekdash/assets/" + asset.file,
              );
            }
          }
        });
      } catch (manifestError) {
        console.warn(
          "[SW] Failed to load manifest for CMDK preload:",
          manifestError,
        );
      }
    }

    if (cmdkAssets.length === 0) {
      return;
    }

    await Promise.allSettled(
      cmdkAssets.map(async (asset) => {
        try {
          await cache.add(asset);
          return { success: true, asset };
        } catch (error) {
          console.warn(
            "[SW] Failed to preload CMDK asset:",
            asset,
            error.message,
          );
          return { success: false, asset, error: error.message };
        }
      }),
    );
  } catch (error) {
    console.error("[SW] Error preloading CMDK:", error);
    throw error; // Re-throw to be caught by caller
  }
}
