/**
 * 📱 PushNotificationService - Service de gestion des notifications push
 * Intégré avec Angular Service Worker et backend Laravel
 */

import { Injectable } from '@angular/core';
import { SwPush } from '@angular/service-worker';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable, of, from } from 'rxjs';
import { catchError, tap, switchMap } from 'rxjs/operators';
import { environment } from '../../environments/environment';

export interface PushSubscriptionData {
  endpoint: string;
  keys: {
    p256dh: string;
    auth: string;
  };
  device_info?: {
    userAgent: string;
    platform: string;
    language: string;
    timezone: string;
  };
}

export interface NotificationPayload {
  title: string;
  body: string;
  icon?: string;
  badge?: string;
  image?: string;
  data?: any;
  actions?: NotificationAction[];
  tag?: string;
  requireInteraction?: boolean;
}

export interface NotificationPreferences {
  pnr_updates: boolean;
  document_ready: boolean;
  system_alerts: boolean;
  task_reminders: boolean;
  error_notifications: boolean;
}

@Injectable({
  providedIn: 'root'
})
export class PushNotificationService {
  private readonly VAPID_PUBLIC_KEY = environment.vapidPublicKey || '';
  private readonly API_BASE_URL = environment.DATA_URL;

  // États observables
  private isSubscribedSubject = new BehaviorSubject<boolean>(false);
  private permissionStateSubject = new BehaviorSubject<NotificationPermission>('default');
  private subscriptionDataSubject = new BehaviorSubject<PushSubscriptionData | null>(null);

  // Observables publiques
  public isSubscribed$ = this.isSubscribedSubject.asObservable();
  public permissionState$ = this.permissionStateSubject.asObservable();
  public subscriptionData$ = this.subscriptionDataSubject.asObservable();

  constructor(
    private swPush: SwPush,
    private http: HttpClient
  ) {
    this.initialize();
  }

  /**
   * Initialisation du service
   */
  private async initialize(): Promise<void> {
    console.log('🔔 PushNotificationService: Initialisation...');

    if (!this.isSupported()) {
      console.warn('⚠️ Push notifications non supportées');
      return;
    }

    // Vérifier l'état des permissions
    this.updatePermissionState();

    // Vérifier si déjà abonné
    this.checkExistingSubscription();

    // Écouter les notifications reçues
    this.listenToNotifications();

    // Écouter les clics sur les notifications
    this.listenToNotificationClicks();
  }

  /**
   * Vérifier si les notifications push sont supportées
   */
  public isSupported(): boolean {
    return this.swPush.isEnabled && 'Notification' in window;
  }

  /**
   * Obtenir l'état actuel des permissions
   */
  public getPermissionState(): NotificationPermission {
    return Notification.permission;
  }

  /**
   * Demander les permissions de notification
   */
  public async requestPermission(): Promise<NotificationPermission> {
    if (!this.isSupported()) {
      throw new Error('Notifications push non supportées');
    }

    try {
      const permission = await Notification.requestPermission();
      this.permissionStateSubject.next(permission);

      console.log('🔔 Permission de notification:', permission);
      return permission;
    } catch (error) {
      console.error('❌ Erreur demande permission:', error);
      throw error;
    }
  }

  /**
   * S'abonner aux notifications push
   */
  public async subscribe(): Promise<PushSubscriptionData | null> {
    if (!this.isSupported()) {
      throw new Error('Notifications push non supportées');
    }

    if (this.getPermissionState() !== 'granted') {
      const permission = await this.requestPermission();
      if (permission !== 'granted') {
        throw new Error('Permission de notification refusée');
      }
    }

    try {
      console.log('🔔 Abonnement aux notifications push...');

      const subscription = await this.swPush.requestSubscription({
        serverPublicKey: this.VAPID_PUBLIC_KEY
      });

      const subscriptionData: PushSubscriptionData = {
        endpoint: subscription.endpoint,
        keys: {
          p256dh: this.arrayBufferToBase64(subscription.getKey('p256dh')!),
          auth: this.arrayBufferToBase64(subscription.getKey('auth')!)
        },
        device_info: this.getDeviceInfo()
      };

      // Envoyer l'abonnement au backend
      await this.sendSubscriptionToBackend(subscriptionData).toPromise();

      this.subscriptionDataSubject.next(subscriptionData);
      this.isSubscribedSubject.next(true);

      console.log('✅ Abonnement réussi:', subscriptionData);
      return subscriptionData;

    } catch (error) {
      console.error('❌ Erreur abonnement:', error);
      throw error;
    }
  }

  /**
   * Se désabonner des notifications push
   */
  public async unsubscribe(): Promise<boolean> {
    try {
      console.log('🔔 Désabonnement des notifications push...');

      const currentSubscription = this.subscriptionDataSubject.value;

      if (currentSubscription) {
        // Informer le backend
        await this.removeSubscriptionFromBackend(currentSubscription.endpoint).toPromise();
      }

      // Désabonner côté client
      const success = await this.swPush.unsubscribe();

      if (success) {
        this.subscriptionDataSubject.next(null);
        this.isSubscribedSubject.next(false);
        console.log('✅ Désabonnement réussi');
      }

      return success;
    } catch (error) {
      console.error('❌ Erreur désabonnement:', error);
      throw error;
    }
  }

  /**
   * Envoyer une notification de test
   */
  public sendTestNotification(): Observable<any> {
    return this.http.post(`${this.API_BASE_URL}push-notifications/test`, {});
  }

  /**
   * Mettre à jour les préférences de notification
   */
  public updatePreferences(preferences: NotificationPreferences): Observable<any> {
    return this.http.put(`${this.API_BASE_URL}push-notifications/preferences`, preferences);
  }

  /**
   * Obtenir les préférences actuelles
   */
  public getPreferences(): Observable<NotificationPreferences> {
    return this.http.get<NotificationPreferences>(`${this.API_BASE_URL}push-notifications/preferences`);
  }

  /**
   * Obtenir l'historique des notifications
   */
  public getNotificationHistory(limit: number = 50): Observable<any[]> {
    return this.http.get<any[]>(`${this.API_BASE_URL}push-notifications/history?limit=${limit}`);
  }

  /**
   * Vérifier l'abonnement existant
   */
  private checkExistingSubscription(): void {
    this.swPush.subscription.subscribe(subscription => {
      if (subscription) {
        const subscriptionData: PushSubscriptionData = {
          endpoint: subscription.endpoint,
          keys: {
            p256dh: this.arrayBufferToBase64(subscription.getKey('p256dh')!),
            auth: this.arrayBufferToBase64(subscription.getKey('auth')!)
          }
        };

        this.subscriptionDataSubject.next(subscriptionData);
        this.isSubscribedSubject.next(true);
        console.log('🔔 Abonnement existant trouvé');
      }
    });
  }

  /**
   * Écouter les notifications reçues
   */
  private listenToNotifications(): void {
    this.swPush.messages.subscribe(message => {
      console.log('🔔 Notification reçue:', message);
      this.handleNotificationReceived(message);
    });
  }

  /**
   * Écouter les clics sur les notifications
   */
  private listenToNotificationClicks(): void {
    this.swPush.notificationClicks.subscribe(event => {
      console.log('🔔 Notification cliquée:', event);
      this.handleNotificationClick(event);
    });
  }

  /**
   * Gérer une notification reçue
   */
  private handleNotificationReceived(notification: any): void {
    // Logique personnalisée pour les notifications reçues
    if (notification.data?.analytics) {
      // Envoyer analytics si nécessaire
      this.trackNotificationReceived(notification);
    }
  }

  /**
   * Gérer le clic sur une notification
   */
  private handleNotificationClick(event: any): void {
    const notificationData = event.notification.data;

    if (notificationData?.url) {
      // Naviguer vers l'URL spécifiée
      window.open(notificationData.url, '_blank');
    }

    if (notificationData?.analytics) {
      // Envoyer analytics du clic
      this.trackNotificationClicked(event.notification);
    }
  }

  /**
   * Envoyer l'abonnement au backend
   */
  private sendSubscriptionToBackend(subscriptionData: PushSubscriptionData): Observable<any> {
    return this.http.post(`${this.API_BASE_URL}push-notifications/subscribe`, subscriptionData)
      .pipe(
        tap(response => console.log('✅ Abonnement envoyé au backend:', response)),
        catchError(error => {
          console.error('❌ Erreur envoi abonnement au backend:', error);
          throw error;
        })
      );
  }

  /**
   * Supprimer l'abonnement du backend
   */
  private removeSubscriptionFromBackend(endpoint: string): Observable<any> {
    return this.http.post(`${this.API_BASE_URL}push-notifications/unsubscribe`, { endpoint })
      .pipe(
        tap(response => console.log('✅ Désabonnement envoyé au backend:', response)),
        catchError(error => {
          console.error('❌ Erreur envoi désabonnement au backend:', error);
          return of(null); // Ne pas échouer le désabonnement client
        })
      );
  }

  /**
   * Mettre à jour l'état des permissions
   */
  private updatePermissionState(): void {
    this.permissionStateSubject.next(this.getPermissionState());
  }

  /**
   * Convertir ArrayBuffer en Base64
   */
  private arrayBufferToBase64(buffer: ArrayBuffer): string {
    const bytes = new Uint8Array(buffer);
    const binary = String.fromCharCode(...bytes);
    return btoa(binary);
  }

  /**
   * Obtenir les informations de l'appareil
   */
  private getDeviceInfo() {
    return {
      userAgent: navigator.userAgent,
      platform: navigator.platform,
      language: navigator.language,
      timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
    };
  }

  /**
   * Tracker la réception d'une notification
   */
  private trackNotificationReceived(notification: any): void {
    // Implémenter le tracking analytics si nécessaire
    console.log('📊 Tracking: Notification reçue');
  }

  /**
   * Tracker le clic sur une notification
   */
  private trackNotificationClicked(notification: any): void {
    // Implémenter le tracking analytics si nécessaire
    console.log('📊 Tracking: Notification cliquée');
  }
}