import { Injectable, ApplicationRef } from '@angular/core';
import { SwUpdate, VersionReadyEvent } from '@angular/service-worker';
import { SnackNotifyService } from '../../components/snack-notify/snack-notify.service';
import { BehaviorSubject, interval, concat, NEVER, Observable } from 'rxjs';
import { filter, first, map, switchMap, tap } from 'rxjs/operators';

export interface PWAInstallPrompt {
  prompt(): Promise<void>;
  userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }>;
}

@Injectable({
  providedIn: 'root'
})
export class PWAService {
  private promptEvent: PWAInstallPrompt | null = null;
  private installPromptSubject = new BehaviorSubject<boolean>(false);
  private updateAvailableSubject = new BehaviorSubject<boolean>(false);

  // Observables públicos
  public installPromptAvailable$ = this.installPromptSubject.asObservable();
  public updateAvailable$ = this.updateAvailableSubject.asObservable();

  constructor(
    private swUpdate: SwUpdate,
    private snackNotifyService: SnackNotifyService,
    private appRef: ApplicationRef
  ) {
    this.initializeService();
  }

  /**
   * Inicializa o serviço PWA
   */
  private initializeService(): void {
    if (this.swUpdate.isEnabled) {
      this.setupUpdateNotifications();
      this.setupInstallPrompt();
      this.setupPeriodicUpdateCheck();
      
      console.log('✅ PWA Service inicializado');
    } else {
      console.warn('⚠️ Service Worker não está habilitado');
    }
  }

  /**
   * Configura notificações de atualização
   */
  private setupUpdateNotifications(): void {
    // Verifica se há uma versão nova disponível
    this.swUpdate.versionUpdates
      .pipe(
        filter((evt): evt is VersionReadyEvent => evt.type === 'VERSION_READY'),
        tap((evt) => {
          console.log('🔄 Nova versão disponível!');
          console.log('Versão atual:', evt.currentVersion);
          console.log('Nova versão:', evt.latestVersion);
        })
      )
      .subscribe((evt) => {
        this.updateAvailableSubject.next(true);
        this.showUpdateNotification();
      });

    // Verifica se houve erro no carregamento
    this.swUpdate.unrecoverable.subscribe(event => {
      console.error('❌ Erro irrecuperável no service worker:', event.reason);
      this.showErrorNotification();
    });
  }

  /**
   * Configura verificação periódica de updates
   */
  private setupPeriodicUpdateCheck(): void {
    // Verifica updates a cada 30 minutos quando a app está estável
    const appIsStable$ = this.appRef.isStable.pipe(first(isStable => isStable === true));
    const everySixHours$ = interval(30 * 60 * 1000); // 30 minutos
    const everyTenMinutesOnceAppIsStable$ = concat(appIsStable$, everySixHours$);

    everyTenMinutesOnceAppIsStable$.subscribe(async () => {
      try {
        const updateFound = await this.swUpdate.checkForUpdate();
        console.log(updateFound ? '🔄 Update encontrado!' : '✅ App atualizado');
      } catch (err) {
        console.error('❌ Erro ao verificar updates:', err);
      }
    });
  }

  /**
   * Configura o prompt de instalação PWA
   */
  private setupInstallPrompt(): void {
    window.addEventListener('beforeinstallprompt', (e: any) => {
      console.log('📲 Prompt de instalação PWA disponível');
      
      // Previne o prompt automático
      e.preventDefault();
      
      // Armazena o evento para uso posterior
      this.promptEvent = e;
      
      // Notifica que o prompt está disponível
      this.installPromptSubject.next(true);
      
      // Mostra notificação ao usuário
      this.showInstallNotification();
    });

    // Detecta quando a app foi instalada
    window.addEventListener('appinstalled', () => {
      console.log('🎉 PWA instalado com sucesso!');
      this.promptEvent = null;
      this.installPromptSubject.next(false);
      this.showInstalledNotification();
    });
  }

  /**
   * Mostra notificação de atualização disponível
   */
  private showUpdateNotification(): void {
    this.snackNotifyService.info('Nova versão disponível! 🚀 Clique para atualizar.', {
      duration: 0 // Não fecha automaticamente
    });
    // Nota: Para ações complexas como "Atualizar", seria necessário expandir o SnackNotify
    // Por enquanto, a notificação informa sobre a atualização
  }

  /**
   * Mostra notificação de instalação disponível
   */
  private showInstallNotification(): void {
    this.snackNotifyService.info('Instalar app na tela inicial? 📲', {
      duration: 8000 // 8 segundos
    });
    // Nota: Para ações como "Instalar", seria necessário expandir o SnackNotify
  }

  /**
   * Mostra notificação de erro
   */
  private showErrorNotification(): void {
    this.snackNotifyService.error('Erro ao carregar atualização. Recarregue a página.', {
      duration: 0 // Não fecha automaticamente
    });
  }

  /**
   * Mostra notificação de instalação concluída
   */
  private showInstalledNotification(): void {
    this.snackNotifyService.success('App instalado com sucesso! 🎉', {
      duration: 3000
    });
  }

  /**
   * Ativa a atualização da aplicação
   */
  public async activateUpdate(): Promise<void> {
    try {
      await this.swUpdate.activateUpdate();
      this.updateAvailableSubject.next(false);
      
      // Marca que deve mostrar splash de changelog na próxima inicialização
      localStorage.setItem('idt_show_update_splash', 'true');
      
      // Recarrega a página para aplicar a nova versão
      this.snackNotifyService.info('Aplicando atualização...', {
        duration: 2000
      });
      
      setTimeout(() => {
        window.location.reload();
      }, 2000);
      
    } catch (error) {
      console.error('❌ Erro ao ativar atualização:', error);
      this.showErrorNotification();
    }
  }

  /**
   * Mostra o prompt de instalação PWA
   */
  public async showInstallPrompt(): Promise<boolean> {
    if (!this.promptEvent) {
      console.warn('⚠️ Prompt de instalação não disponível');
      return false;
    }

    try {
      // Mostra o prompt
      await this.promptEvent.prompt();
      
      // Aguarda a escolha do usuário
      const { outcome } = await this.promptEvent.userChoice;
      
      console.log(`🎯 Resultado do prompt: ${outcome}`);
      
      if (outcome === 'accepted') {
        console.log('✅ Usuário aceitou instalar a PWA');
        return true;
      } else {
        console.log('❌ Usuário rejeitou instalar a PWA');
        return false;
      }
      
    } catch (error) {
      console.error('❌ Erro ao mostrar prompt de instalação:', error);
      return false;
    } finally {
      // Limpa o prompt após o uso
      this.promptEvent = null;
      this.installPromptSubject.next(false);
    }
  }

  /**
   * Verifica se a aplicação está rodando como PWA instalada
   */
  public isInstalledPWA(): boolean {
    return window.matchMedia('(display-mode: standalone)').matches ||
           window.matchMedia('(display-mode: fullscreen)').matches ||
           (window.navigator as any).standalone === true;
  }

  /**
   * Verifica se o browser suporta PWA
   */
  public isPWASupported(): boolean {
    return 'serviceWorker' in navigator && 'PushManager' in window;
  }

  /**
   * Força verificação de updates
   */
  public async checkForUpdate(): Promise<boolean> {
    if (!this.swUpdate.isEnabled) {
      console.warn('⚠️ Service Worker não habilitado');
      return false;
    }

    try {
      const updateAvailable = await this.swUpdate.checkForUpdate();
      console.log(updateAvailable ? '🔄 Update encontrado!' : '✅ App já está atualizado');
      return updateAvailable;
    } catch (error) {
      console.error('❌ Erro ao verificar updates:', error);
      return false;
    }
  }

  /**
   * Obtém informações da versão atual
   */
  public async getCurrentVersion(): Promise<string> {
    if (!this.swUpdate.isEnabled) {
      return 'Service Worker não habilitado';
    }

    try {
      const current = await this.swUpdate.checkForUpdate();
      return current ? 'Nova versão disponível' : 'Versão atual';
    } catch (error) {
      return 'Erro ao verificar versão';
    }
  }

  /**
   * Métodos públicos para componentes
   */
  public get canInstall(): boolean {
    return this.promptEvent !== null;
  }

  public get isUpdateAvailable(): boolean {
    return this.updateAvailableSubject.value;
  }

  public get isInstallPromptAvailable(): boolean {
    return this.installPromptSubject.value;
  }
} 