import { CommonModule } from '@angular/common';
import { HttpEventType } from '@angular/common/http';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { ConvenzioniStrutture, Struttura } from '@api/models';
import { FileSizePipe, FileSizePipeBinary } from '@core/pipes';
import { UtilsService } from '@core/services/utils.service';
import { environment } from '@environments/environment';
import { RxState } from '@rx-angular/state';
import { some, sumBy } from 'lodash';
import { ToastMessageOptions } from 'primeng/api';
import { ButtonModule } from 'primeng/button';
import { DialogModule } from 'primeng/dialog';
import { FileUpload, FileUploadModule } from 'primeng/fileupload';
import { MessagesModule } from 'primeng/messages';
import { ToastModule } from 'primeng/toast';
import {
  catchError,
  EMPTY,
  endWith,
  map,
  Observable,
  of,
  startWith,
  switchMap,
  tap,
} from 'rxjs';
import { UploadDocumentazioneService } from './upload-documentazione.service';

interface State {
  isLoading: boolean;
  isSaving: boolean;

  struttura: Struttura;
  convenzione: ConvenzioniStrutture;
  filesSizeSumExceed: boolean;

  visible: boolean;
  submitted: boolean;
}

@Component({
  selector: 'app-upload-documentazione',
  imports: [
    CommonModule,
    FileUploadModule,
    DialogModule,
    ReactiveFormsModule,
    MessagesModule,
    ButtonModule,
    FileSizePipe,
    FileSizePipeBinary,
    ToastModule,
  ],
  templateUrl: './upload-documentazione.component.html',
  styleUrls: ['./upload-documentazione.component.scss'],
  providers: [RxState],
})
export class UploadDocumentazioneComponent {
  model$: Observable<State>;
  MAX_FILE_SIZE = environment.MAX_FILE_SIZE;
  showTransitionOptions = '500ms';
  messages: ToastMessageOptions[] = [];

  @Input() set struttura(struttura: Struttura | undefined) {
    if (!struttura) return;
    this.state.set({ struttura });
  }
  @Input() set convenzione(convenzione: ConvenzioniStrutture) {
    if (!convenzione) return;
    this.state.set({ convenzione });
  }
  @Input() set visible(visible: boolean) {
    this.state.set({ visible: visible ?? false });
  }

  @Output() close: EventEmitter<boolean> = new EventEmitter<boolean>();
  @Output() documentazioneUploaded: EventEmitter<{ message: string }> =
    new EventEmitter<{ message: string }>();

  constructor(
    public readonly state: RxState<State>,
    private utilsService: UtilsService,
    private uploadDocumentazioneService: UploadDocumentazioneService,
  ) {
    this.model$ = this.state.select();
    this.state.set({
      filesSizeSumExceed: false,
    });
    const isLoading$ = this.state.select(['isSaving'], (s) => some(s));
    this.state.connect('isLoading', isLoading$);
  }

  progressReport(fileUpload: FileUpload, $event: any) {
    fileUpload.progress = $event;
  }

  removeFile(event: Event, fileUpload: FileUpload, file: File) {
    const index = fileUpload.files.indexOf(file);
    fileUpload.remove(event, index);
    this.checkIfTooLarge(fileUpload.files);
  }

  resetUpload(fileUpload: FileUpload) {
    this.state.set({ submitted: false });
    fileUpload.onProgress.emit({
      originalEvent: {
        type: HttpEventType.UploadProgress,
        loaded: 0,
        total: 100,
      },
      progress: 0,
    });
    fileUpload.clear();
  }

  selectFile(files: File[]): void {
    this.checkIfTooLarge(files);
  }

  changeVisible(value: boolean) {
    this.state.set({ visible: value });
    this.close.emit(value);
  }

  cancel() {
    this.changeVisible(false);
  }

  checkIfTooLarge(files: File[]) {
    const sumSize = sumBy(files, (f) => f.size);
    const maxSize = this.formatBytes(environment.MAX_FILE_SIZE);
    if (sumSize > maxSize) {
      this.state.set({ filesSizeSumExceed: true });
    } else {
      this.state.set({ filesSizeSumExceed: false });
    }
  }

  formatBytes(bytes: number, decimals = 2) {
    if (bytes === 0) {
      return 0;
    }
    const k = 1024;
    const dm = decimals < 0 ? 0 : decimals;
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) * Math.pow(1000, i);
  }

  save(fileUpload: FileUpload) {
    this.state.set({ submitted: true });
    const tooLarge = this.state.get('filesSizeSumExceed');
    if (tooLarge || !fileUpload.files.length) {
      return;
    }
    const params = {
      id: this.state.get('struttura').id,
      idConvenzione: this.state.get('convenzione').id,
      body: {
        files: fileUpload.files,
      },
    };
    fileUpload.disabled = true;

    const caricaDocumentazione$ = of(EMPTY).pipe(
      switchMap(() =>
        this.uploadDocumentazioneService.uploadDocumentazione(params).pipe(
          catchError(() => {
            fileUpload.disabled = false;
            this.messages = [
              {
                severity: 'error',
                summary: 'Qualcosa è andato storto!',
                life: 5000,
                detail: 'Riprovare più tardi',
              },
            ];
            // fileUpload.onProgress.emit(0);
            return of(null);
          }),
          tap((res) => {
            if (!res) return;
            // if (res.status === 'progress' && fileUpload) {
            // fileUpload.onProgress.emit(res['message']);
            // } else if (res?.status === 201) {
            // }
            fileUpload.disabled = false;
            this.changeVisible(false);
            this.documentazioneUploaded.emit(res);
          }),
          map(() => ({})),
          startWith({ isSaving: true }),
          endWith({ isSaving: false }),
        ),
      ),
    );
    this.state.connect(caricaDocumentazione$);
  }
}
