/**
 * PWA Utilities
 * Service worker registration, update handling, and install prompt management
 */

export interface PWAStatus {
  isInstalled: boolean;
  isStandalone: boolean;
  canInstall: boolean;
  isOnline: boolean;
  updateAvailable: boolean;
}

interface BeforeInstallPromptEvent extends Event {
  readonly platforms: string[];
  readonly userChoice: Promise<{
    outcome: 'accepted' | 'dismissed';
    platform: string;
  }>;
  prompt(): Promise<void>;
}

let deferredPrompt: BeforeInstallPromptEvent | null = null;
let swRegistration: ServiceWorkerRegistration | null = null;

// Check if running as installed PWA
export function isInstalled(): boolean {
  if (typeof window === 'undefined') return false;

  return (
    window.matchMedia('(display-mode: standalone)').matches ||
    (window.navigator as any).standalone === true ||
    document.referrer.includes('android-app://')
  );
}

// Check if running in standalone mode
export function isStandalone(): boolean {
  if (typeof window === 'undefined') return false;
  return window.matchMedia('(display-mode: standalone)').matches;
}

// Check if app can be installed
export function canInstall(): boolean {
  return deferredPrompt !== null;
}

// Get current PWA status
export function getPWAStatus(): PWAStatus {
  return {
    isInstalled: isInstalled(),
    isStandalone: isStandalone(),
    canInstall: canInstall(),
    isOnline: typeof navigator !== 'undefined' ? navigator.onLine : true,
    updateAvailable: false, // Updated by service worker
  };
}

// Prompt user to install PWA
export async function promptInstall(): Promise<boolean> {
  if (!deferredPrompt) {
    return false;
  }

  await deferredPrompt.prompt();
  const { outcome } = await deferredPrompt.userChoice;

  deferredPrompt = null;

  return outcome === 'accepted';
}

// Register service worker
export async function registerServiceWorker(): Promise<ServiceWorkerRegistration | null> {
  if (typeof window === 'undefined' || !('serviceWorker' in navigator)) {
    return null;
  }

  try {
    const registration = await navigator.serviceWorker.register('/sw.js', {
      scope: '/',
    });

    swRegistration = registration;

    // Check for updates periodically
    setInterval(() => {
      registration.update();
    }, 60 * 60 * 1000); // Check every hour

    // Handle updates
    registration.addEventListener('updatefound', () => {
      const newWorker = registration.installing;
      if (!newWorker) return;

      newWorker.addEventListener('statechange', () => {
        if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
          // New update available
          dispatchPWAEvent('updateavailable');
        }
      });
    });

    return registration;
  } catch (error) {
    console.error('Service worker registration failed:', error);
    return null;
  }
}

// Apply pending update
export async function applyUpdate(): Promise<void> {
  if (!swRegistration?.waiting) return;

  // Tell service worker to skip waiting
  swRegistration.waiting.postMessage({ type: 'SKIP_WAITING' });

  // Reload page after new service worker takes over
  navigator.serviceWorker.addEventListener('controllerchange', () => {
    window.location.reload();
  });
}

// Unregister service worker
export async function unregisterServiceWorker(): Promise<boolean> {
  if (!swRegistration) return false;

  const success = await swRegistration.unregister();
  if (success) {
    swRegistration = null;
  }
  return success;
}

// Clear all caches
export async function clearCaches(): Promise<void> {
  if (typeof caches === 'undefined') return;

  const cacheNames = await caches.keys();
  await Promise.all(cacheNames.map((name) => caches.delete(name)));
}

// Subscribe to push notifications
export async function subscribeToPush(vapidPublicKey: string): Promise<PushSubscription | null> {
  if (!swRegistration) {
    console.error('Service worker not registered');
    return null;
  }

  try {
    const subscription = await swRegistration.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: urlBase64ToUint8Array(vapidPublicKey),
    });

    return subscription;
  } catch (error) {
    console.error('Push subscription failed:', error);
    return null;
  }
}

// Unsubscribe from push notifications
export async function unsubscribeFromPush(): Promise<boolean> {
  if (!swRegistration) return false;

  try {
    const subscription = await swRegistration.pushManager.getSubscription();
    if (subscription) {
      return await subscription.unsubscribe();
    }
    return true;
  } catch {
    return false;
  }
}

// Check push notification permission
export function getPushPermission(): NotificationPermission {
  if (typeof Notification === 'undefined') return 'denied';
  return Notification.permission;
}

// Request push notification permission
export async function requestPushPermission(): Promise<NotificationPermission> {
  if (typeof Notification === 'undefined') return 'denied';
  return await Notification.requestPermission();
}

// Initialize PWA
export function initPWA(): () => void {
  if (typeof window === 'undefined') return () => {};

  // Listen for install prompt
  const handleBeforeInstallPrompt = (e: Event) => {
    e.preventDefault();
    deferredPrompt = e as BeforeInstallPromptEvent;
    dispatchPWAEvent('caninstall');
  };

  // Listen for app installed
  const handleAppInstalled = () => {
    deferredPrompt = null;
    dispatchPWAEvent('installed');
  };

  // Listen for online/offline
  const handleOnline = () => dispatchPWAEvent('online');
  const handleOffline = () => dispatchPWAEvent('offline');

  window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
  window.addEventListener('appinstalled', handleAppInstalled);
  window.addEventListener('online', handleOnline);
  window.addEventListener('offline', handleOffline);

  // Register service worker
  registerServiceWorker();

  return () => {
    window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
    window.removeEventListener('appinstalled', handleAppInstalled);
    window.removeEventListener('online', handleOnline);
    window.removeEventListener('offline', handleOffline);
  };
}

// PWA event dispatcher
function dispatchPWAEvent(type: string, detail?: any): void {
  window.dispatchEvent(new CustomEvent(`pwa:${type}`, { detail }));
}

// Utility to convert VAPID key
function urlBase64ToUint8Array(base64String: string): Uint8Array {
  const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
  const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');

  const rawData = window.atob(base64);
  const outputArray = new Uint8Array(rawData.length);

  for (let i = 0; i < rawData.length; ++i) {
    outputArray[i] = rawData.charCodeAt(i);
  }
  return outputArray;
}

// Share API wrapper
export async function share(data: ShareData): Promise<boolean> {
  if (!navigator.share) {
    return false;
  }

  try {
    await navigator.share(data);
    return true;
  } catch (error) {
    if ((error as Error).name !== 'AbortError') {
      console.error('Share failed:', error);
    }
    return false;
  }
}

// Check if Web Share API is supported
export function canShare(data?: ShareData): boolean {
  if (!navigator.share) return false;
  if (!data) return true;
  return navigator.canShare?.(data) ?? true;
}
