// Service Worker Registration Utility
// Handles registration, updates, and messaging with the service worker

export interface ServiceWorkerManager {
  register: () => Promise<ServiceWorkerRegistration | null>;
  unregister: () => Promise<boolean>;
  update: () => Promise<void>;
  getCacheSize: () => Promise<number>;
  clearCache: () => Promise<void>;
  cacheUrls: (urls: string[]) => Promise<void>;
  isSupported: boolean;
  isRegistered: boolean;
  registration: ServiceWorkerRegistration | null;
}

class ServiceWorkerManagerImpl implements ServiceWorkerManager {
  private _registration: ServiceWorkerRegistration | null = null;
  private _isRegistered = false;

  get isSupported(): boolean {
    try {
      // In some iframe contexts, service worker might be restricted
      // Test if we can actually access the service worker API
      if (navigator.serviceWorker) {
        // Try to access properties that might be restricted in iframes
        return true;
      }
      return false;
    } catch (error) {
      console.warn(
        "[SW Manager] Service Worker access restricted (likely in iframe):",
        error
      );
      return false;
    }
  }

  get isRegistered(): boolean {
    return this._isRegistered;
  }

  get registration(): ServiceWorkerRegistration | null {
    return this._registration;
  }

  async register(): Promise<ServiceWorkerRegistration | null> {
    if (!this.isSupported) {
      console.warn("[SW Manager] Service Workers not supported");
      return null;
    }

    try {
      // Register the service worker
      this._registration = await navigator.serviceWorker.register("http://localhost:8080/sw.js", {
        scope: "*",
        updateViaCache: "imports",
      });

      console.log(
        "[SW Manager] Service Worker registered:",
        this._registration.scope
      );
      this._isRegistered = true;

      // Handle updates
      this._registration.addEventListener("updatefound", () => {
        const newWorker = this._registration?.installing;
        if (newWorker) {
          console.log("[SW Manager] New service worker found, installing...");

          newWorker.addEventListener("statechange", () => {
            if (
              newWorker.state === "installed" &&
              navigator.serviceWorker.controller
            ) {
              console.log(
                "[SW Manager] New service worker installed, prompting update..."
              );
              this.promptForUpdate();
            }
          });
        }
      });

      // Listen for controlling service worker changes
      navigator.serviceWorker.addEventListener("controllerchange", () => {
        console.log("[SW Manager] Controller changed, reloading page...");
        window.location.reload();
      });

      // Handle messages from service worker
      navigator.serviceWorker.addEventListener("message", (event) => {
        this.handleServiceWorkerMessage(event);
      });

      return this._registration;
    } catch (error) {
      console.error("[SW Manager] Service Worker registration failed:", error);
      return null;
    }
  }

  async unregister(): Promise<boolean> {
    if (!this._registration) {
      return false;
    }

    try {
      const result = await this._registration.unregister();
      if (result) {
        this._registration = null;
        this._isRegistered = false;
        console.log("[SW Manager] Service Worker unregistered");
      }
      return result;
    } catch (error) {
      console.error(
        "[SW Manager] Service Worker unregistration failed:",
        error
      );
      return false;
    }
  }

  async update(): Promise<void> {
    if (!this._registration) {
      throw new Error("Service Worker not registered");
    }

    try {
      await this._registration.update();
      console.log("[SW Manager] Service Worker update check completed");
    } catch (error) {
      console.error("[SW Manager] Service Worker update failed:", error);
      throw error;
    }
  }

  async getCacheSize(): Promise<number> {
    const result = await this.sendMessage({ type: "GET_CACHE_SIZE" });
    return (result as { size: number }).size || 0;
  }

  async clearCache(): Promise<void> {
    await this.sendMessage({ type: "CLEAR_CACHE" });
  }

  async cacheUrls(urls: string[]): Promise<void> {
    await this.sendMessage({ type: "CACHE_URLS", data: { urls } });
  }

  private sendMessage(message: Record<string, unknown>): Promise<unknown> {
    return new Promise((resolve, reject) => {
      if (!navigator.serviceWorker.controller) {
        reject(new Error("No active service worker"));
        return;
      }

      const messageChannel = new MessageChannel();

      messageChannel.port1.onmessage = (event) => {
        resolve(event.data);
      };

      // Set up a timeout to reject if no response
      const timeout = setTimeout(() => {
        reject(new Error("Service worker message timeout"));
      }, 5000);

      messageChannel.port1.onmessage = (event) => {
        clearTimeout(timeout);
        resolve(event.data);
      };

      navigator.serviceWorker.controller.postMessage(message, [
        messageChannel.port2,
      ]);
    });
  }

  private handleServiceWorkerMessage(event: MessageEvent) {
    const { type, data } = event.data;

    switch (type) {
      case "CACHE_SIZE":
        console.log("[SW Manager] Cache size:", data.size, "items");
        break;

      case "CACHE_CLEARED":
        console.log("[SW Manager] Cache cleared");
        break;

      case "URLS_CACHED":
        console.log("[SW Manager] URLs cached successfully");
        break;

      default:
        console.log(
          "[SW Manager] Unknown message from service worker:",
          event.data
        );
    }
  }

  private promptForUpdate() {
    // You can customize this to show a UI notification
    if (confirm("A new version is available. Reload to update?")) {
      this.skipWaiting();
    }
  }

  private skipWaiting() {
    if (this._registration?.waiting) {
      this._registration.waiting.postMessage({ type: "SKIP_WAITING" });
    }
  }
}

// Create and export singleton instance
export const serviceWorkerManager: ServiceWorkerManager =
  new ServiceWorkerManagerImpl();

// Auto-register on import (you can remove this if you prefer manual registration)
export const registerServiceWorker = async (): Promise<void> => {
  if (serviceWorkerManager.isSupported) {
    try {
      await serviceWorkerManager.register();
      console.log("[SW Manager] Service Worker registration completed");
    } catch (error) {
      console.error("[SW Manager] Service Worker registration failed:", error);
    }
  }
};

// Utility to check if app is running offline
export const isOffline = (): boolean => {
  return !navigator.onLine;
};

// Utility to check if app is running in iframe
export const isInIframe = (): boolean => {
  try {
    return window.self !== window.top;
  } catch {
    return true;
  }
};

// Cache preloading utility
export const preloadCriticalAssets = async (urls: string[]): Promise<void> => {
  if (serviceWorkerManager.isRegistered) {
    try {
      await serviceWorkerManager.cacheUrls(urls);
      console.log("[SW Manager] Critical assets preloaded");
    } catch (error) {
      console.error("[SW Manager] Failed to preload assets:", error);
    }
  }
};

// Network status utilities
export const setupNetworkStatusHandlers = (): void => {
  window.addEventListener("online", () => {
    console.log("[SW Manager] App is online");
    document.body.classList.remove("offline");
  });

  window.addEventListener("offline", () => {
    console.log("[SW Manager] App is offline");
    document.body.classList.add("offline");
  });
};

export default serviceWorkerManager;
