/**
 * Push Notification Service for InstaRecipe Queue System
 * 
 * Handles web push notifications for background processing updates
 * when users are not actively viewing the application.
 */

import webpush from 'web-push';
import { queueConfig } from '../queue/config';

interface PushSubscription {
  endpoint: string;
  keys: {
    p256dh: string;
    auth: string;
  };
}

interface NotificationPayload {
  title?: string;
  body: string;
  type: 'success' | 'error' | 'progress';
  itemId: string;
  recipeName?: string;
  tag?: string;
  requireInteraction?: boolean;
  analytics?: any;
}

class PushNotificationService {
  private subscriptions = new Map<string, PushSubscription>();
  private vapidKeys: { publicKey: string; privateKey: string } | null = null;

  constructor() {
    this.loadVapidKeys();
    
    // Configure web-push with VAPID details
    if (this.vapidKeys) {
      webpush.setVapidDetails(
        queueConfig.push.vapidEmail,
        this.vapidKeys.publicKey,
        this.vapidKeys.privateKey
      );
    }
  }

  /**
   * Load VAPID keys for push notifications
   * In production, these should be stored securely and loaded from environment
   */
  private loadVapidKeys() {
    // Load from config module which uses SvelteKit's $env/dynamic/private
    this.vapidKeys = {
      publicKey: queueConfig.push.vapidPublicKey,
      privateKey: queueConfig.push.vapidPrivateKey
    };
  }

  /**
   * Get the public VAPID key for client-side subscription
   */
  getPublicVapidKey(): string | null {
    return this.vapidKeys?.publicKey || null;
  }

  /**
   * Subscribe a client to push notifications
   */
  async subscribe(clientId: string, subscription: PushSubscription): Promise<void> {
    console.log(`[PushService] Subscribing client ${clientId}`);
    this.subscriptions.set(clientId, subscription);
    
    // In production, store subscriptions in database
    // For development, we'll keep them in memory
  }

  /**
   * Unsubscribe a client from push notifications
   */
  async unsubscribe(clientId: string): Promise<void> {
    console.log(`[PushService] Unsubscribing client ${clientId}`);
    this.subscriptions.delete(clientId);
  }

  /**
   * Send notification to all subscribed clients
   */
  async sendNotification(payload: NotificationPayload): Promise<void> {
    if (this.subscriptions.size === 0) {
      console.log('[PushService] No subscriptions, skipping notification');
      return;
    }

    console.log(`[PushService] Sending notification to ${this.subscriptions.size} subscribers`);
    console.log(`[PushService] Notification payload:`, payload);

    // In a real implementation, this would use web-push library
    // For development/demo purposes, we'll simulate the notification
    const notificationData = {
      ...payload,
      timestamp: new Date().toISOString()
    };

    for (const [clientId, subscription] of this.subscriptions) {
      try {
        await this.sendToSubscription(subscription, notificationData);
        console.log(`[PushService] ✓ Sent notification to client ${clientId}`);
      } catch (error) {
        console.error(`[PushService] ✗ Failed to send to client ${clientId}:`, error);
        // Remove invalid subscriptions
        this.subscriptions.delete(clientId);
      }
    }
  }

  /**
   * Send notification to specific subscription
   */
  private async sendToSubscription(subscription: PushSubscription, data: any): Promise<void> {
    try {
      const payload = JSON.stringify(data);
      
      await webpush.sendNotification(
        {
          endpoint: subscription.endpoint,
          keys: {
            p256dh: subscription.keys.p256dh,
            auth: subscription.keys.auth
          }
        },
        payload,
        {
          TTL: 60 * 60 * 24, // 24 hours
        }
      );
      
      console.log(`[PushService] ✓ Sent notification to ${subscription.endpoint.substring(0, 50)}...`);
    } catch (error) {
      // Check if subscription is expired/invalid
      if ((error as any).statusCode === 410) {
        console.warn(`[PushService] Subscription expired: ${subscription.endpoint.substring(0, 50)}...`);
        throw new Error('Subscription expired');
      }
      
      console.error('[PushService] Failed to send notification:', {
        endpoint: subscription.endpoint.substring(0, 50) + '...',
        error: error instanceof Error ? error.message : String(error)
      });
      throw error;
    }
  }

  /**
   * Send success notification when recipe extraction completes
   */
  async notifySuccess(itemId: string, recipeName?: string, tandoorUrl?: string): Promise<void> {
    const payload: NotificationPayload = {
      type: 'success',
      itemId,
      recipeName,
      body: recipeName 
        ? `Recipe "${recipeName}" has been extracted and saved successfully!`
        : 'Your recipe extraction is complete and ready to view.',
      tag: `recipe-success-${itemId}`,
      requireInteraction: true,
      analytics: {
        event: 'recipe_extraction_complete',
        itemId,
        timestamp: Date.now()
      }
    };

    if (tandoorUrl) {
      payload.body += ' View it in Tandoor.';
    }

    await this.sendNotification(payload);
  }

  /**
   * Send error notification when recipe extraction fails
   */
  async notifyError(itemId: string, error: string): Promise<void> {
    const payload: NotificationPayload = {
      type: 'error',
      itemId,
      body: `Recipe extraction failed: ${error}. Tap to retry.`,
      tag: `recipe-error-${itemId}`,
      requireInteraction: true,
      analytics: {
        event: 'recipe_extraction_failed',
        itemId,
        error,
        timestamp: Date.now()
      }
    };

    await this.sendNotification(payload);
  }

  /**
   * Send progress notification for long-running extractions
   */
  async notifyProgress(itemId: string, phase: string): Promise<void> {
    const payload: NotificationPayload = {
      type: 'progress',
      itemId,
      body: `Recipe extraction in progress: ${phase}`,
      tag: `recipe-progress-${itemId}`,
      requireInteraction: false,
      analytics: {
        event: 'recipe_extraction_progress',
        itemId,
        phase,
        timestamp: Date.now()
      }
    };

    await this.sendNotification(payload);
  }

  /**
   * Get subscription count for monitoring
   */
  getSubscriptionCount(): number {
    return this.subscriptions.size;
  }

  /**
   * Clear all subscriptions (for testing/cleanup)
   */
  clearAllSubscriptions(): void {
    console.log('[PushService] Clearing all subscriptions');
    this.subscriptions.clear();
  }
}

// Singleton instance
export const pushNotificationService = new PushNotificationService();

export type { PushSubscription, NotificationPayload };