import {
  Component,
  OnInit,
  ContentChildren,
  QueryList,
  AfterContentInit,
  Inject,
  Input,
  Output,
  EventEmitter,
  OnDestroy,
  forwardRef,
} from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { trigger, transition, style, animate } from '@angular/animations';
import { UiDropdownItemDirective } from './dropdown-item.directive';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { fromEvent, Subscription } from 'rxjs';

@Component({
  selector: 'app-ui-dropdown',
  templateUrl: 'dropdown.component.html',
  styleUrls: ['dropdown.component.scss'],
  exportAs: 'uiDropdown',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => UiDropdownComponent),
      multi: true,
    },
  ],
  animations: [
    trigger('dropdownAnimation', [
      transition(':enter', [
        style({ transform: 'translateY(-60px)', opacity: 0 }),
        animate('200ms', style({ transform: 'translateY(0)', opacity: 1 })),
      ]),
      transition(':leave', [style({ opacity: '*' }), animate('200ms', style({ opacity: 0 }))]),
    ]),
  ],
})
export class UiDropdownComponent implements OnInit, OnDestroy, ControlValueAccessor, AfterContentInit {
  visible = false;

  @ContentChildren(UiDropdownItemDirective)
  options: QueryList<UiDropdownItemDirective>;

  @Input()
  value: any;

  @Output()
  valueChange = new EventEmitter<any>();

  optionChangesSubscription: Subscription;

  windowKeyEventsSubscription: Subscription;
  windowKeyEvents$ = fromEvent(this.document, 'keyup');

  onChange = (value) => {};
  onTouched = () => {};

  constructor(@Inject(DOCUMENT) private document: Document) {}

  ngOnInit() {}

  ngOnDestroy() {
    if (this.optionChangesSubscription) {
      this.optionChangesSubscription.unsubscribe();
    }
    this.unsubscribeKeyboardEvents();
  }

  ngAfterContentInit() {
    this.options.forEach((option) => {
      option.registerOnSelect((value) => this.selectValue(value));
    });

    this.optionChangesSubscription = this.options.changes.subscribe((changes) => {
      this.options.forEach((option) => {
        option.registerOnSelect((value) => this.selectValue(value));
      });
    });
  }

  toggle(visible?: boolean) {
    if (typeof visible === 'boolean') {
      this.visible = visible;
    } else {
      this.visible = !this.visible;
    }

    if (this.visible) {
      this.subscribeKeyboardEvents();
    } else {
      this.unsubscribeKeyboardEvents();
    }
  }

  subscribeKeyboardEvents() {
    if (this.windowKeyEventsSubscription) {
      this.unsubscribeKeyboardEvents();
    }
    this.windowKeyEventsSubscription = this.windowKeyEvents$.subscribe((event: KeyboardEvent) => {
      const activeElementIndex = this.options.toArray().findIndex((o) => o.focused);

      if (event.code === 'ArrowUp' || event.code === 'ArrowDown') {
        let nextElementIndex = activeElementIndex;
        if (event.code === 'ArrowUp') {
          if (activeElementIndex > 0) {
            nextElementIndex--;
          }
        } else if (event.code === 'ArrowDown') {
          if (activeElementIndex + 1 < this.options.length) {
            nextElementIndex++;
          }
        }
        const option = this.options.find((item, index) => nextElementIndex === index);
        if (option) {
          option.focus();
        }
      }
    });
  }

  unsubscribeKeyboardEvents() {
    if (this.windowKeyEventsSubscription) {
      this.windowKeyEventsSubscription.unsubscribe();
      delete this.windowKeyEventsSubscription;
    }
  }

  open() {
    this.visible = true;
  }

  close() {
    this.visible = false;
  }

  selectValue(value: any) {
    console.log({ value });
    this.value = value;
    this.onChange(value);
    this.valueChange.emit(value);
    this.onTouched();
    this.visible = false;
  }

  writeValue(obj: any): void {
    this.value = obj;
  }

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

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

  setDisabledState?(isDisabled: boolean): void {}
}
