import { Component, OnInit } from '@angular/core';
import { marker } from '@biesbjerg/ngx-translate-extract-marker';
import { merge, Subject } from 'rxjs';
import { AuthService } from 'src/app/shared/server-services/auth.service';
import {
  CordovaService,
  NotificationCommands,
  NotificationParam,
  ProgressNotificationParam
} from 'src/app/shared/services/cordova.service';
import {
  CollectionManagerType,
  downloadCollectionErrorObserver,
  downloadCollectionFinishedObserver,
  downloadCollectionProgressObserver,
  downloadCollectionStartedObserver,
  downloadErrorObserver,
  downloadFinishedObserver,
  DownloadManagerService,
  downloadProgressObserver,
  downloadStartedObserver,
  FileManagerType,
  fileRemovedObserver,
  uploadErrorObserver,
  uploadFinishedObserver,
  uploadProgressObserver,
  uploadStartedObserver
} from 'src/app/shared/services/download-manager-service';

@Component({
  selector: 'app-progress-bar',
  templateUrl: './progress-bar.component.html',
  styleUrls: ['./progress-bar.component.scss'],
})
export class ProgressBarComponent implements OnInit {
  public downloadProgress: (FileManagerType | CollectionManagerType)[] = [];
  public overallProgress: number;
  public showLoadingBar: boolean = false;
  public errorHappened: boolean = false;

  private uploadOrDownloadEvents = merge(
    downloadStartedObserver,
    downloadProgressObserver,
    downloadFinishedObserver,
    downloadCollectionStartedObserver,
    downloadCollectionProgressObserver,
    downloadCollectionFinishedObserver,
    uploadStartedObserver,
    uploadProgressObserver,
    uploadFinishedObserver
  );

  private finishedEvents = merge(
    downloadFinishedObserver,
    downloadCollectionFinishedObserver,
    uploadFinishedObserver
  );

  private progressUpdatedEvent = new Subject();

  private errorEvents = merge(
    downloadErrorObserver,
    downloadCollectionErrorObserver,
    uploadErrorObserver
  );

  constructor(
    private cordovaService: CordovaService,
    private downloadManagerService: DownloadManagerService,
    private authSerivce: AuthService
  ) {}

  ngOnInit(): void {
    this.createFileEventHandlers();
    this.createEventHandlers();
    if (this.cordovaService.isOnApp()) this.createMobileEventHandlers();
  }

  private createFileEventHandlers(): void {
    merge(
      downloadStartedObserver,
      uploadStartedObserver,
      downloadCollectionStartedObserver
    ).subscribe((file) => {
      this.downloadProgress.push(file);
    });
  }

  private createEventHandlers(): void {
    this.errorEvents.subscribe((errorObj: { error: any; file: FileManagerType }) => {
      this.errorHappened = true;
    });

    this.uploadOrDownloadEvents.subscribe(() => {
      this.updateLoadingBarVisibility();
      this.updateOverallProgress();
    });

    fileRemovedObserver.subscribe((removedFile: FileManagerType) => {
      this.downloadProgress = this.downloadProgress.filter((file) => file != removedFile);
      this.errorHappened = this.downloadProgress.some((f) => f.error);

      this.updateOverallProgress();
      this.updateLoadingBarVisibility();
    });

    this.authSerivce.onLogout(() => {
      this.updateLoadingBarVisibility();
    });
  }

  private createMobileEventHandlers(): void {
    this.errorEvents.subscribe((errorObj: { error: any; file: FileManagerType }) => {
      this.sendErrorNotification();
    });

    this.progressUpdatedEvent.subscribe(() => {
      this.sendProgressNotification();
    });

    fileRemovedObserver.subscribe(() => {
      if (this.downloadProgress.length === 0)
        this.cordovaService.clearNotification(CordovaService.PROGRESS_NOTIFICATION_ID);
    });

    this.finishedEvents.subscribe(() => {
      if (this.overallProgress === 100) this.cordovaService.playNotificationSound();
    });
  }

  private updateOverallProgress(): void {
    //should we calculate progress of active downloads, or all files within download page??
    // let activeFiles = this.downloadProgress.filter((file) => file.progress != 100);
    if (this.downloadProgress.length === 0) return;

    if (this.downloadProgress.every((file) => file.done)) {
      this.overallProgress = 100;
      this.progressUpdatedEvent.next();
      this.downloadProgress = [];
      return;
    }

    let activeFiles = this.downloadProgress;
    let progressCount: number = 0;
    let max: number = activeFiles.length * 100;
    activeFiles.map((file) => file.progress).forEach((prog) => (progressCount += prog));

    this.overallProgress = (progressCount / max) * 100;

    this.progressUpdatedEvent.next();
    this.errorHappened = this.downloadProgress.some((f) => f.error);
  }

  private updateLoadingBarVisibility(): void {
    this.showLoadingBar =
      this.downloadManagerService.isDownloadInProgress() ||
      this.downloadManagerService.isUploadInProgress();
  }

  sendProgressNotification(): void {
    var activeFiles = this.downloadProgress;
    var message;
    var title;

    if (this.overallProgress < 100) {
      title = activeFiles.length == 1 ? marker('File progress') : marker('Overall progress');
      message =
        activeFiles.length == 1
          ? marker('File: ') + activeFiles[0]?.file?.fullName ?? activeFiles[0]?.path
          : marker('Multiple files');
    } else {
      title = activeFiles.length == 1 ? marker('File ready') : marker('Files ready');
      message = activeFiles.length == 1 ? marker('File: ') + activeFiles[0]?.file?.fullName : null;
    }

    var progressNotification: ProgressNotificationParam = {
      id: CordovaService.PROGRESS_NOTIFICATION_ID, //all progress tracking runs on a single notification
      text: message,
      title: title,
      data: {
        command: NotificationCommands.NAVIGATE,
        to: '/download',
      },
      progressBar:
        this.overallProgress < 100
          ? { value: this.overallProgress, enabled: true }
          : { enabled: false },
    };

    // this.cordovaService.callProgressNotification(progressNotification);
  }

  sendErrorNotification(): void {
    var errorFiles = this.downloadProgress.filter((f) => f.error);
    var message;
    var title;

    if (errorFiles.length === 1) {
      title = marker('File error');
      message = marker('File: ') + errorFiles[0]?.file?.fullName;
    } else {
      title = marker('Several file error');
      message = marker('Error happened with several files.');
    }

    var progressErrorNotification: NotificationParam = {
      id: CordovaService.PROGRESS_ERROR_NOTIFICATION_ID,
      text: message,
      title: title,
      data: {
        command: NotificationCommands.NAVIGATE,
        to: '/download',
      },
      group: 'progress',
    };

    this.cordovaService.callProgressErrorNotification(progressErrorNotification);

    //if no other files are in progress, then remove the progress tracker because error happened
    if (this.downloadProgress.filter((f) => !f.error && f.progress < 100).length == 0)
      this.cordovaService.clearNotification(CordovaService.PROGRESS_NOTIFICATION_ID);
  }
}
