import {
  Directive, EventEmitter, OnDestroy, OnInit, Output
} from '@angular/core';
import {
  FormControl, FormGroup
} from '@angular/forms';
import { Subject } from 'rxjs';
import { pairwise, startWith, takeUntil } from 'rxjs/operators';
import { FormService } from './form.service';
import { ValueChangeElement } from './form';
import { ElementComponent } from './element-component.directive';

@Directive()
export abstract class FormElementComponent extends ElementComponent implements OnInit, OnDestroy {
  @Output() formValueChanged = new EventEmitter<ValueChangeElement>();
  parentForm!: FormGroup;
  defaultValue!: string | number | boolean | undefined;
  elementFormControl!: FormControl;
  private ngUnsubscribe = new Subject<void>();

  constructor(private formService: FormService) {
    super();
  }

  ngOnInit(): void {
    this.formService.registerFormControl({
      id: this.elementModel.id,
      formControl: new FormControl(this.elementModel.value),
      formGroup: this.parentForm
    });
    this.elementFormControl = this.formControl;
    this.elementFormControl.valueChanges
      .pipe(
        startWith(this.elementModel.value),
        pairwise(),
        takeUntil(this.ngUnsubscribe)
      )
      .subscribe(([prevValue, nextValue] : [string | number | boolean | undefined, string | number | boolean]) => {
        if (nextValue != null) { // invalid input on number fields generates event with null TODO find a better solution
          this.formValueChanged.emit({ id: this.elementModel.id, values: [prevValue, nextValue] });
        }
      });
  }

  private get formControl(): FormControl {
    // workaround for editor
    return (this.parentForm) ?
      this.parentForm.controls[this.elementModel.id] as FormControl :
      new FormControl({});
  }

  updateFormValue(newValue: string | number | boolean): void {
    this.elementFormControl?.setValue(newValue, { emitEvent: false });
  }

  ngOnDestroy(): void {
    this.ngUnsubscribe.next();
    this.ngUnsubscribe.complete();
  }
}