import {
  Directive,
  Injectable,
} from '@angular/core';
import {
  AbstractControl,
  AsyncValidator,
  NG_VALIDATORS,
  ValidationErrors,
} from '@angular/forms';
import {
  Observable,
  of,
  timer as observableTimer,
} from 'rxjs';
import {
  map,
  switchMap,
  take,
} from 'rxjs/operators';

import { MetadataFieldDataService } from '../../core/data/metadata-field-data.service';
import { PaginatedList } from '../../core/data/paginated-list.model';
import { RemoteData } from '../../core/data/remote-data';
import { MetadataField } from '../../core/metadata/metadata-field.model';
import { getFirstSucceededRemoteData } from '../../core/shared/operators';

/**
 * Directive for validating if a ngModel value is a valid metadata field
 */
@Directive({
  selector: '[ngModel][dsMetadataFieldValidator]',
  // We add our directive to the list of existing validators
  providers: [
    { provide: NG_VALIDATORS, useExisting: MetadataFieldValidator, multi: true },
  ],
  standalone: true,
})
@Injectable({ providedIn: 'root' })
export class MetadataFieldValidator implements AsyncValidator {

  constructor(private metadataFieldService: MetadataFieldDataService) {
  }

  /**
   * The function that checks if the form control's value is currently valid
   * @param control The FormControl
   */
  validate(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
    const resTimer = observableTimer(500).pipe(
      switchMap(() => {
        if (!control.value) {
          return of({ invalidMetadataField: { value: control.value } });
        }
        const mdFieldNameParts = control.value.split('.');
        if (mdFieldNameParts.length < 2) {
          return of({ invalidMetadataField: { value: control.value } });
        }

        const res = this.metadataFieldService.findByExactFieldName(control.value)
          .pipe(
            getFirstSucceededRemoteData(),
            map((matchingFieldRD: RemoteData<PaginatedList<MetadataField>>) => {
              if (matchingFieldRD.payload.pageInfo.totalElements === 0) {
                return { invalidMetadataField: { value: control.value } };
              } else if (matchingFieldRD.payload.pageInfo.totalElements === 1) {
                return null;
              }
            }),
          );

        res.pipe(take(1)).subscribe();

        return res;
      }),
    );
    resTimer.pipe(take(1)).subscribe();
    return resTimer;
  }
}