import {
  AbstractControl,
  FormArray,
  FormControl, FormControlDirective, FormControlName,
  FormGroup,
  ValidationErrors,
  ValidatorFn,
  Validators
} from '@angular/forms';

// nativeElement добавляется во все контролы - за счет этого можно обратиться к nativeElement и вызвать focus()
// Проверить производительность. Если будет тормозить - убирать - но также нужно убрать все вызовы setFocusToInvalidControl
const originFormControlNgOnChanges = FormControlDirective.prototype.ngOnChanges;
FormControlDirective.prototype.ngOnChanges = function () {
  this.form.nativeElement = this.valueAccessor._elementRef.nativeElement;
  return originFormControlNgOnChanges.apply(this, arguments);
};

const originFormControlNameNgOnChanges = FormControlName.prototype.ngOnChanges;
FormControlName.prototype.ngOnChanges = function () {
  const result = originFormControlNameNgOnChanges.apply(this, arguments);
  this.control.nativeElement = this.valueAccessor._elementRef ? this.valueAccessor._elementRef.nativeElement : null;
  return result;
};

export class FormHelper {

  public static isInvalid(fg: FormGroup, controlName: string): boolean {
    const control = fg.controls[controlName];
    if (!control) {
      return true;
    }
    return control.invalid && (control.dirty || control.touched || FormHelper.isSubmitted(fg));
  }

  public static isSubmitted(fg: AbstractControl): boolean {
    while (fg) {
      if ((fg as any).submitted) {
        return true;
      }
      fg = fg.parent;
    }
    return false;
  }

  // Этим мы боремся с вполне допустимыми значениями в пикере типа 12345-01-01, из за чего это все не может корректно
  // прийти на сервер и Spring JSON Serializer падает с соответствующей ошибкой, а пользователю приходит ничего не значащая
  // для него ошибка 500. Валидатор должнен стояти на каждом из контролов
  public static validateDateTimePicker(): ValidatorFn {
    return Validators.pattern(/^\d{4}-\d{2}-\d{2}(T\w.*)*$/);
  }

  public static findFirstInvalidControlRecursive(formToInvestigate: FormGroup | FormArray): FormControl {
    let invalidControl = null;
    const recursiveFunc = (form: FormGroup | FormArray) => {
      for (const field of Object.keys(form.controls)) {
        const control = form.get(field);
        if (!invalidControl && control instanceof FormGroup) {
          recursiveFunc(control);
        } else if (!invalidControl && control instanceof FormArray) {
          recursiveFunc(control);
        } else if (!invalidControl && control.invalid && field === 'dateTo' && form.get('dateToIncluded').value) {
          // если invalid контрол - это dateTo и dateToIncluded заполнен, то нужно перейти к контролу dateToIncluded
          invalidControl = form.get('dateToIncluded');
          break;
        } else if (!invalidControl && control.invalid) {
          invalidControl = control;
          break;
        }
      }
    };
    recursiveFunc(formToInvestigate);
    return invalidControl;
  }

  public static setFocusToInvalidControl(form: FormGroup) {
    const invalidControl = FormHelper.findFirstInvalidControlRecursive(form);
    if (invalidControl && (<any>invalidControl).nativeElement) {
      (<any>invalidControl).nativeElement.focus();
    }
  }

  public static isInvalidControl(control: AbstractControl): boolean {
    if (!control) {
      return true;
    }
    return control.invalid && (control.dirty || control.touched || FormHelper.isSubmitted(control.parent));
  }
}
