/*
 * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den
 * Ministerpräsidenten des Landes Schleswig-Holstein
 * Staatskanzlei
 * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
 *
 * Lizenziert unter der EUPL, Version 1.2 oder - sobald
 * diese von der Europäischen Kommission genehmigt wurden -
 * Folgeversionen der EUPL ("Lizenz");
 * Sie dürfen dieses Werk ausschließlich gemäß
 * dieser Lizenz nutzen.
 * Eine Kopie der Lizenz finden Sie hier:
 *
 * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
 *
 * Sofern nicht durch anwendbare Rechtsvorschriften
 * gefordert oder in schriftlicher Form vereinbart, wird
 * die unter der Lizenz verbreitete Software "so wie sie
 * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
 * ausdrücklich oder stillschweigend - verbreitet.
 * Die sprachspezifischen Genehmigungen und Beschränkungen
 * unter der Lizenz sind dem Lizenztext zu entnehmen.
 */
import { HttpErrorResponse, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { createEmptyStateResource, createErrorStateResource, createStateResource, EMPTY_STRING, getMessageForIssue, isNotNil, isUnprocessableEntity, isValidationFieldFileSizeExceedError, sanitizeFileName, StateResource } from '@goofy-client/tech-shared';
import { SnackBarService } from '@goofy-client/ui';
import { Resource, ResourceUri } from '@ngxp/rest';
import { saveAs } from 'file-saver';
import { isNil } from 'lodash-es';
import { Observable, of, throwError } from 'rxjs';
import { catchError, map, mergeMap, startWith } from 'rxjs/operators';
import { BinaryFileListResource, BinaryFileResource } from './binary-file.model';
import { BinaryFileRepository } from './binary-file.repository';

@Injectable({ providedIn: 'root' })
export class BinaryFileService {

	constructor(private repository: BinaryFileRepository, private snackbarService: SnackBarService) { }

	public uploadFile(resource: Resource, linkRel: string, file: File): Observable<StateResource<BinaryFileResource>> {
		return this.repository.uploadFile(resource, linkRel, file).pipe(
			mergeMap((response: HttpResponse<void>) => this.getFile(response.headers.get('Location'))),
			catchError(errorResponse => this.handleError(errorResponse.error)),
			startWith(createEmptyStateResource<BinaryFileResource>(true)));
	}

	private handleError(errorResponse: HttpErrorResponse): Observable<StateResource<any>> {
		return of(this.handleErrorByStatus(errorResponse));
	}

	handleErrorByStatus(error: HttpErrorResponse): StateResource<any> {
		if (isUnprocessableEntity(error.status) && isValidationFieldFileSizeExceedError(error.error)) {
			this.snackbarService.showError(getMessageForIssue(EMPTY_STRING, error.error.issues[0]));
			return createErrorStateResource(error.error);
		}
		throwError({ error });
	}

	public downloadFile(file: BinaryFileResource, fileNamePrefix: string): Observable<StateResource<any>> {
		return this.repository.download(file).pipe(
			map(data => this.saveBinaryFile(data, file, fileNamePrefix)),
			startWith(createEmptyStateResource(true)));
	}

	saveBinaryFile(data: any, file: BinaryFileResource, fileNamePrefix: string): StateResource<Blob> {
		if (isNil(data)) {
			return createEmptyStateResource(true);
		}
		this.save(data, this.buildFileName(file, fileNamePrefix));
		return createStateResource(data);
	}

	private buildFileName(file: BinaryFileResource, fileNamePrefix: string): string {
		if(isNotNil(fileNamePrefix)){
			return sanitizeFileName(`${fileNamePrefix}_${file.name}`);
		}
		return file.name;
	}

	save(data: any, fileName: string): void {
		saveAs(data, fileName)
	}

	getFile(uri: ResourceUri): Observable<StateResource<BinaryFileResource>> {
		return this.repository.getFile(uri).pipe(
			map(fileList => createStateResource(fileList)),
			startWith(createEmptyStateResource<any>(true)));
	}

	public getFiles(resource: Resource, linkRel: string): Observable<StateResource<BinaryFileListResource>> {
		return this.repository.getFiles(resource, linkRel).pipe(
			map(fileList => createStateResource(fileList)),
			startWith(createEmptyStateResource<BinaryFileListResource>(true)));
	}
}