// Push notification service for browser push API
import { config, configUtils } from '@/lib/config'

export interface PushSubscriptionData {
  endpoint: string
  keys: {
    p256dh: string
    auth: string
  }
}

export interface NotificationPayload {
  title: string
  body: string
  icon?: string
  badge?: string
  data?: Record<string, any>
  tag?: string
  requireInteraction?: boolean
  actions?: Array<{
    action: string
    title: string
    icon?: string
  }>
}

export class PushNotificationService {
  private static instance: PushNotificationService

  static getInstance(): PushNotificationService {
    if (!this.instance) {
      this.instance = new PushNotificationService()
    }
    return this.instance
  }

  // Check if push notifications are supported
  isSupported(): boolean {
    return 'serviceWorker' in navigator && 
           'PushManager' in window && 
           'Notification' in window
  }

  // Check if push notifications are configured
  isConfigured(): boolean {
    return configUtils.hasPushConfig()
  }

  // Get current notification permission
  getPermission(): NotificationPermission {
    return Notification.permission
  }

  // Request notification permission
  async requestPermission(): Promise<NotificationPermission> {
    if (!this.isSupported()) {
      throw new Error('Push notifications are not supported')
    }

    const permission = await Notification.requestPermission()
    console.log('Notification permission:', permission)
    return permission
  }

  // Convert Uint8Array to base64 URL-safe string
  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
  }

  // Subscribe to push notifications
  async subscribe(): Promise<PushSubscriptionData> {
    if (!this.isSupported()) {
      throw new Error('Push notifications are not supported')
    }

    if (!this.isConfigured()) {
      throw new Error('Push notifications are not configured. Missing VAPID key.')
    }

    if (this.getPermission() !== 'granted') {
      const permission = await this.requestPermission()
      if (permission !== 'granted') {
        throw new Error('Push notification permission denied')
      }
    }

    try {
      const registration = await navigator.serviceWorker.ready
      
      // Check if already subscribed
      const existingSubscription = await registration.pushManager.getSubscription()
      if (existingSubscription) {
        console.log('Using existing push subscription')
        return this.subscriptionToData(existingSubscription)
      }

      // Create new subscription
      console.log('Creating new push subscription with VAPID key:', config.push.vapidPublicKey.substring(0, 20) + '...')
      
      const applicationServerKey = this.urlBase64ToUint8Array(config.push.vapidPublicKey)
      console.log('VAPID key converted to Uint8Array, length:', applicationServerKey.length)
      
      const subscription = await registration.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey
      })

      const subscriptionData = this.subscriptionToData(subscription)
      
      console.log('Push subscription created successfully:', {
        endpoint: subscriptionData.endpoint,
        hasKeys: !!subscriptionData.keys.p256dh && !!subscriptionData.keys.auth
      })
      return subscriptionData

    } catch (error) {
      console.error('Failed to subscribe to push notifications:', error)
      
      // Provide more specific error information
      if (error instanceof DOMException) {
        if (error.name === 'InvalidStateError') {
          console.error('VAPID key format issue detected. Please check your VAPID public key.')
        } else if (error.name === 'NotAllowedError') {
          console.error('Push notifications blocked by user or browser policy.')
        } else if (error.name === 'NotSupportedError') {
          console.error('Push notifications not supported by this browser.')
        }
      }
      
      throw error
    }
  }

  // Unsubscribe from push notifications
  async unsubscribe(): Promise<void> {
    try {
      const registration = await navigator.serviceWorker.ready
      const subscription = await registration.pushManager.getSubscription()
      
      if (subscription) {
        await subscription.unsubscribe()
        console.log('Push subscription removed')
      }
    } catch (error) {
      console.error('Failed to unsubscribe from push notifications:', error)
      throw error
    }
  }

  // Get current subscription
  async getSubscription(): Promise<PushSubscriptionData | null> {
    try {
      const registration = await navigator.serviceWorker.ready
      const subscription = await registration.pushManager.getSubscription()
      
      if (subscription) {
        return this.subscriptionToData(subscription)
      }
      
      return null
    } catch (error) {
      console.error('Failed to get push subscription:', error)
      return null
    }
  }

  // Convert PushSubscription to our data format
  private subscriptionToData(subscription: PushSubscription): PushSubscriptionData {
    const keys = subscription.getKey('p256dh')
    const auth = subscription.getKey('auth')
    
    if (!keys || !auth) {
      throw new Error('Failed to get subscription keys')
    }

    return {
      endpoint: subscription.endpoint,
      keys: {
        p256dh: btoa(String.fromCharCode(...new Uint8Array(keys))),
        auth: btoa(String.fromCharCode(...new Uint8Array(auth)))
      }
    }
  }

  // Show a local notification (for testing)
  async showLocalNotification(payload: NotificationPayload): Promise<void> {
    if (!this.isSupported()) {
      throw new Error('Notifications are not supported')
    }

    if (this.getPermission() !== 'granted') {
      await this.requestPermission()
    }

    if (this.getPermission() !== 'granted') {
      throw new Error('Notification permission denied')
    }

    // Send message to service worker to show notification
    const registration = await navigator.serviceWorker.ready
    
    if (registration.active) {
      registration.active.postMessage({
        type: 'SHOW_NOTIFICATION',
        payload
      })
    } else {
      // Fallback: show browser notification directly
      new Notification(payload.title, {
        body: payload.body,
        icon: payload.icon || '/pwa-192x192.png',
        badge: payload.badge || '/pwa-192x192.png',
        tag: payload.tag,
        requireInteraction: payload.requireInteraction,
        data: payload.data
      })
    }
  }

  // Check if user is subscribed to push notifications
  async isSubscribed(): Promise<boolean> {
    const subscription = await this.getSubscription()
    return subscription !== null
  }
}

// Export singleton instance
export const pushService = PushNotificationService.getInstance()