/*
 * 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 { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';

import { Resource } from '@ngxp/rest';
import { isNil } from 'lodash-es';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { StateResource, hasStateResourceError } from '../resource/resource.util';
import { ApiError, HttpError, InvalidParam, Issue, ProblemDetail } from '../tech.model';
import { isNotUndefined } from '../tech.util';
import {
  setInvalidParamValidationError,
  setIssueValidationError,
} from '../validation/tech.validation.util';

export abstract class AbstractFormService {
  form: UntypedFormGroup;
  pathPrefix: string;
  source: any;

  private readonly PROBLEM_DETAIL_INVALID_PARAMS_KEY: string = 'invalid-params';

  constructor(public formBuilder: UntypedFormBuilder) {
    this.form = this.initForm();
  }

  protected abstract initForm(): UntypedFormGroup;

  public submit(): Observable<StateResource<Resource>> {
    return this.doSubmit().pipe(map((result) => this.handleResponse(result)));
  }

  protected abstract doSubmit(): Observable<StateResource<Resource>>;

  handleResponse(result: StateResource<Resource>): StateResource<Resource> {
    if (result.loading) return result;
    if (hasStateResourceError(result)) {
      this.handleError(result.error);
    }
    return result;
  }

  handleError(error: HttpError): void {
    if (this.isApiError(error)) {
      this.setErrorByApiError(<ApiError>error);
    }
    if (this.isProblemDetail(error)) {
      this.setErrorByProblemDetail(<ProblemDetail>error);
    }
  }

  private isApiError(error: HttpError): boolean {
    return isNotUndefined((<ApiError>error).issues);
  }

  private isProblemDetail(error: HttpError): boolean {
    return isNotUndefined((<ProblemDetail>error)[this.PROBLEM_DETAIL_INVALID_PARAMS_KEY]);
  }

  setErrorByApiError(apiError: ApiError): void {
    apiError.issues.forEach((issue: Issue) =>
      setIssueValidationError(this.form, issue, this.getPathPrefix()),
    );
  }

  setErrorByProblemDetail(error: ProblemDetail): void {
    error[this.PROBLEM_DETAIL_INVALID_PARAMS_KEY].forEach((invalidParam: InvalidParam) => {
      setInvalidParamValidationError(this.form, invalidParam, this.getPathPrefix());
    });
  }

  protected abstract getPathPrefix(): string;

  public patch(valueToPatch: any): void {
    this.form.reset();
    this.form.patchValue(valueToPatch);

    this.source = valueToPatch;
  }

  getFormValue(): any {
    return this.form.value;
  }

  protected getSourceValue(): any {
    return this.source;
  }

  public isPatch(): boolean {
    return !isNil(this.source);
  }
}