import { Directive, ElementRef, forwardRef, HostBinding, HostListener, Input, Renderer2 } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { UiFormControlDirective } from '@ui/form-control';

@Directive({
  selector: 'input[uiDateInput]',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => UiDateInputDirective),
      multi: true,
    },
    {
      provide: UiFormControlDirective,
      useExisting: UiDateInputDirective,
    },
  ],
})
export class UiDateInputDirective extends UiFormControlDirective<any> implements ControlValueAccessor {
  @Input()
  @HostBinding('attr.type')
  type: string;

  get valueEmpty(): boolean {
    return !!this.value;
  }

  readonly today = new Date();

  value: string;

  private onChange = (_: any) => {};
  private onTouched = () => {};

  constructor(private elementRef: ElementRef, private renderer: Renderer2) {
    super();
  }

  writeValue(obj: string | Date): void {
    if (!!obj) {
      this.setValue(new Date(obj), false);
    } else {
      this.setValue('', false);
    }
    this.renderInputValue();
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  @HostListener('keyup', ['$event.target.value'])
  setValue(value: string | Date, emitEvent = true) {
    if (value instanceof Date) {
      this.value = this.format(value);
    } else {
      this.value = value;
    }

    if (emitEvent) {
      const dateStruct = this.getDateStruct(this.value);

      if (dateStruct) {
        this.onChange(this.parseDateStruct(dateStruct).toJSON());
      } else {
        this.onChange(this.value);
      }
    }

    this.onTouched();
  }

  getDateStruct(stringDate: string): { date: number; month: number; year: number } | undefined {
    if (!!stringDate) {
      const parts = stringDate.trim().split('.');
      if (parts.length === 3) {
        const date = +parts[0];
        const month = +parts[1];
        const year = +parts[2];

        if (date >= 1 && date <= 31 && month >= 1 && month <= 12 && year >= 1900 && year <= this.today.getFullYear()) {
          return { date, month, year };
        }
      }
    }
    return undefined;
  }

  parseDateStruct({ year, month, date }: { date: number; month: number; year: number }) {
    return new Date(year, month - 1, date);
  }

  format(date: Date): string {
    if (date) {
      return `${this.prepend(date.getDate())}.${this.prepend(date.getMonth() + 1)}.${date.getFullYear()}`;
    }
    return '';
  }

  prepend(num: number, digits: number = 2) {
    let out = String(num);

    while (out.length < digits) {
      out = `0${out}`;
    }

    return out;
  }

  renderInputValue() {
    this.renderer.setAttribute(this.elementRef.nativeElement, 'value', this.value);
  }

  clear(): void {
    this.setValue(undefined);
    this.renderInputValue();
  }

  focus(): void {
    setTimeout(() => {
      this.elementRef?.nativeElement?.click()?.focus();
    }, 0);
  }

  @HostListener('focus')
  onFocus() {
    this.focused.emit(true);
  }

  @HostListener('blur')
  onBlur() {
    this.onTouched();
    this.focused.emit(false);
  }
}
