/**
 * PWA Manager
 * 
 * Utility class for managing PWA installation, updates, and lifecycle
 * Extracted from Holy Habit PWA management features
 */

import { PWAInstallationState, PWALifecycleEvents, PWACapabilities, PWAError } from '../types/PWA';

export class PWAManager {
  private installPrompt: any = null;
  private registration: ServiceWorkerRegistration | null = null;
  private events: PWALifecycleEvents = {};

  constructor(events?: PWALifecycleEvents) {
    this.events = events || {};
    this.setupEventListeners();
  }

  /**
   * Initialize PWA manager and register service worker
   * 
   * @param serviceWorkerPath - Path to service worker file
   * @returns Registration promise
   */
  async initialize(serviceWorkerPath: string = '/service-worker.js'): Promise<ServiceWorkerRegistration | null> {
    if (!this.isServiceWorkerSupported()) {
      console.warn('Service Workers are not supported in this browser');
      return null;
    }

    try {
      this.registration = await navigator.serviceWorker.register(serviceWorkerPath);
      
      console.log('Service Worker registered successfully');
      
      // Setup update checking
      this.setupUpdateHandling();
      
      // Fire install event if available
      if (this.events.onInstall) {
        this.events.onInstall();
      }

      return this.registration;
    } catch (error) {
      console.error('Service Worker registration failed:', error);
      throw new PWAError('Service Worker registration failed', 'SERVICE_WORKER_FAILED', error);
    }
  }

  /**
   * Get PWA installation state
   * 
   * @returns Current installation state
   */
  getInstallationState(): PWAInstallationState {
    return {
      isInstallable: this.installPrompt !== null,
      isInstalled: this.isStandalone(),
      installPrompt: this.installPrompt,
      userChoice: undefined // Will be set after user interaction
    };
  }

  /**
   * Prompt user to install PWA
   * 
   * @returns User choice result
   */
  async promptInstall(): Promise<'accepted' | 'dismissed' | null> {
    if (!this.installPrompt) {
      throw new PWAError('Install prompt not available', 'INSTALL_FAILED');
    }

    try {
      // Show the install prompt
      this.installPrompt.prompt();
      
      // Wait for user choice
      const choiceResult = await this.installPrompt.userChoice;
      
      if (choiceResult.outcome === 'accepted') {
        console.log('User accepted the install prompt');
        this.installPrompt = null; // Clear the prompt
        return 'accepted';
      } else {
        console.log('User dismissed the install prompt');
        return 'dismissed';
      }
    } catch (error) {
      console.error('Install prompt failed:', error);
      throw new PWAError('Install prompt failed', 'INSTALL_FAILED', error);
    }
  }

  /**
   * Check if PWA is running in standalone mode
   * 
   * @returns True if running as standalone app
   */
  isStandalone(): boolean {
    return window.matchMedia('(display-mode: standalone)').matches ||
           (window.navigator as any).standalone === true;
  }

  /**
   * Get PWA capabilities
   * 
   * @returns Object with capability flags
   */
  getCapabilities(): PWACapabilities {
    return {
      serviceWorker: 'serviceWorker' in navigator,
      pushNotifications: 'PushManager' in window && 'serviceWorker' in navigator,
      backgroundSync: 'serviceWorker' in navigator && 'sync' in window.ServiceWorkerRegistration.prototype,
      indexedDB: 'indexedDB' in window,
      webShare: 'share' in navigator,
      installPrompt: this.installPrompt !== null,
      standalone: this.isStandalone()
    };
  }

  /**
   * Check for PWA updates
   * 
   * @returns True if update is available
   */
  async checkForUpdates(): Promise<boolean> {
    if (!this.registration) {
      return false;
    }

    try {
      await this.registration.update();
      
      // Check if there's a waiting service worker
      if (this.registration.waiting) {
        if (this.events.onUpdateAvailable) {
          this.events.onUpdateAvailable('new-version');
        }
        return true;
      }

      return false;
    } catch (error) {
      console.error('Update check failed:', error);
      return false;
    }
  }

  /**
   * Apply pending update
   */
  async applyUpdate(): Promise<void> {
    if (!this.registration?.waiting) {
      throw new PWAError('No update available to apply', 'SERVICE_WORKER_FAILED');
    }

    // Send message to service worker to skip waiting
    this.registration.waiting.postMessage({ type: 'SKIP_WAITING' });
    
    // Reload the page to activate new service worker
    window.location.reload();
  }

  /**
   * Request push notification permission
   * 
   * @returns Permission state
   */
  async requestNotificationPermission(): Promise<NotificationPermission> {
    if (!('Notification' in window)) {
      throw new PWAError('Notifications not supported', 'NOTIFICATION_DENIED');
    }

    if (Notification.permission === 'granted') {
      return 'granted';
    }

    if (Notification.permission === 'denied') {
      throw new PWAError('Notifications denied by user', 'NOTIFICATION_DENIED');
    }

    // Request permission
    const permission = await Notification.requestPermission();
    
    if (permission === 'denied') {
      throw new PWAError('User denied notification permission', 'NOTIFICATION_DENIED');
    }

    return permission;
  }

  /**
   * Subscribe to push notifications
   * 
   * @param vapidKey - VAPID public key
   * @returns Push subscription
   */
  async subscribeToPush(vapidKey: string): Promise<PushSubscription> {
    if (!this.registration) {
      throw new PWAError('Service Worker not registered', 'SERVICE_WORKER_FAILED');
    }

    await this.requestNotificationPermission();

    try {
      const subscription = await this.registration.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: this.urlBase64ToUint8Array(vapidKey)
      });

      return subscription;
    } catch (error) {
      console.error('Push subscription failed:', error);
      throw new PWAError('Push subscription failed', 'NOTIFICATION_DENIED', error);
    }
  }

  /**
   * Get current push subscription
   * 
   * @returns Current subscription or null
   */
  async getPushSubscription(): Promise<PushSubscription | null> {
    if (!this.registration) {
      return null;
    }

    return await this.registration.pushManager.getSubscription();
  }

  /**
   * Unsubscribe from push notifications
   * 
   * @returns True if successful
   */
  async unsubscribeFromPush(): Promise<boolean> {
    const subscription = await this.getPushSubscription();
    
    if (subscription) {
      return await subscription.unsubscribe();
    }

    return false;
  }

  /**
   * Show offline notification
   * 
   * @param message - Notification message
   */
  showOfflineNotification(message: string = 'You are currently offline'): void {
    if ('Notification' in window && Notification.permission === 'granted') {
      new Notification('Connection Status', {
        body: message,
        icon: '/assets/icons/icon-192x192.png',
        tag: 'offline-status'
      });
    }
  }

  /**
   * Show online notification
   * 
   * @param message - Notification message
   */
  showOnlineNotification(message: string = 'You are back online'): void {
    if ('Notification' in window && Notification.permission === 'granted') {
      new Notification('Connection Status', {
        body: message,
        icon: '/assets/icons/icon-192x192.png',
        tag: 'online-status'
      });
    }
  }

  /**
   * Setup event listeners for PWA lifecycle
   */
  private setupEventListeners(): void {
    // Install prompt event
    window.addEventListener('beforeinstallprompt', (e) => {
      e.preventDefault();
      this.installPrompt = e;
      console.log('Install prompt available');
    });

    // App installed event
    window.addEventListener('appinstalled', () => {
      console.log('PWA was installed');
      this.installPrompt = null;
    });

    // Online/offline events
    window.addEventListener('online', () => {
      console.log('App is online');
      if (this.events.onOnline) {
        this.events.onOnline();
      }
      this.showOnlineNotification();
    });

    window.addEventListener('offline', () => {
      console.log('App is offline');
      if (this.events.onOffline) {
        this.events.onOffline();
      }
      this.showOfflineNotification();
    });
  }

  /**
   * Setup service worker update handling
   */
  private setupUpdateHandling(): void {
    if (!this.registration) return;

    // Check for updates periodically
    setInterval(() => {
      this.registration?.update();
    }, 60000); // Check every minute

    // Handle service worker state changes
    this.registration.addEventListener('updatefound', () => {
      const newWorker = this.registration?.installing;
      
      if (newWorker) {
        newWorker.addEventListener('statechange', () => {
          if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
            // New service worker available
            if (this.events.onUpdateAvailable) {
              this.events.onUpdateAvailable('update-available');
            }
          }
        });
      }
    });

    // Handle service worker messages
    navigator.serviceWorker.addEventListener('message', (event) => {
      if (event.data.type === 'UPDATE_APPLIED') {
        if (this.events.onUpdateApplied) {
          this.events.onUpdateApplied();
        }
      }
    });
  }

  /**
   * Check if service workers are supported
   */
  private isServiceWorkerSupported(): boolean {
    return 'serviceWorker' in navigator;
  }

  /**
   * Convert VAPID key to Uint8Array
   */
  private 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;
  }

  /**
   * Update event handlers
   */
  updateEvents(newEvents: Partial<PWALifecycleEvents>): void {
    this.events = { ...this.events, ...newEvents };
  }

  /**
   * Get service worker registration
   */
  getRegistration(): ServiceWorkerRegistration | null {
    return this.registration;
  }

  /**
   * Clear install prompt (useful for custom install flows)
   */
  clearInstallPrompt(): void {
    this.installPrompt = null;
  }
}