import { CommonModule } from '@angular/common';
import {
  AfterViewInit,
  ChangeDetectionStrategy,
  Component,
  ElementRef,
  EventEmitter,
  forwardRef,
  HostBinding,
  HostListener,
  Input,
  OnDestroy,
  OnInit,
  Output,
  ViewChild,
} from '@angular/core';
import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
import { BranchDTO, BranchType } from '@generated/swagger/checkout-api';
import { UiAutocompleteComponent, UiAutocompleteModule } from '@ui/autocomplete';
import { UiCommonModule } from '@ui/common';
import { isNaN } from 'lodash';
import { asapScheduler, Subject } from 'rxjs';
import { takeUntil, withLatestFrom } from 'rxjs/operators';
import { BranchSelectorStore } from './branch-selector.store';
import { AuthService } from '@core/auth';
import { IconComponent } from '@shared/components/icon';

@Component({
  selector: 'shared-branch-selector',
  templateUrl: 'branch-selector.component.html',
  styleUrls: ['branch-selector.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  host: { class: 'shared-branch-selector', tabindex: '0' },
  providers: [
    BranchSelectorStore,
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => BranchSelectorComponent),
      multi: true,
    },
  ],
  imports: [CommonModule, FormsModule, IconComponent, UiAutocompleteModule, UiCommonModule],
  exportAs: 'sharedBranchSelector',
})
export class BranchSelectorComponent implements OnInit, OnDestroy, AfterViewInit, ControlValueAccessor {
  @ViewChild(UiAutocompleteComponent, { read: UiAutocompleteComponent, static: false })
  autocompleteComponent: UiAutocompleteComponent;

  // Wird benötigt um mit Keys das Autocomplete bedienen zu können ohne die Filterung bei Änderung der Query zu triggern
  complete = new Subject<string>();

  filteredBranches$ = this.store.filteredBranches$;

  query$ = this.store.query$;

  @Input()
  set online(online: boolean) {
    this.store.setOnline(online);
  }

  @Input()
  set orderingEnabled(orderingEnabled: boolean) {
    this.store.setOrderingEnabled(orderingEnabled);
  }

  @Input()
  set shippingEnabled(shippingEnabled: boolean) {
    this.store.setShippingEnabled(shippingEnabled);
  }

  @Input()
  set filterCurrentBranch(filterCurrentBranch: boolean) {
    this.store.setFilterCurrentBranch(filterCurrentBranch);
  }

  @Input()
  set orderBy(orderBy: 'name' | 'distance') {
    this.store.setOrderBy(orderBy);
  }

  // Für Zielfilialen dürfen nur BranchType === 1 Filialen in der Liste erscheinen
  // Für Bestellfilialen und Zielfilialen gelten auch alle BranchTypes und kann undefined gelassen werden
  @Input()
  set branchType(branchType: BranchType) {
    this.store.setBranchType(branchType);
  }

  @Input()
  get value(): BranchDTO {
    return this._value;
  }
  set value(value: BranchDTO) {
    this.setBranch(value);
  }
  private _value: BranchDTO;

  private _onDestroy$ = new Subject<void>();

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

  @Input()
  disabled = false;

  @Input()
  placeholder?: string = 'Filiale suchen';

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

  @ViewChild('branchInput')
  branchInput: ElementRef<HTMLInputElement>;

  get isOpen() {
    return this.autocompleteComponent?.open ?? false;
  }

  @HostBinding('class.focused')
  focused = false;

  constructor(
    public store: BranchSelectorStore,
    private _auth: AuthService,
    private _elementRef: ElementRef,
  ) {}

  writeValue(obj: any): void {
    if (obj?.id) {
      this.value = this.store.getBranchById(Number(obj.id));
    } else {
      this.value = obj;
    }
  }

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

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

  setDisabledState?(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

  ngOnInit(): void {
    this.store.loadBranches();
  }

  ngAfterViewInit() {
    this.initAutocomplete();
  }

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

  initAutocomplete() {
    this.complete
      .pipe(takeUntil(this._onDestroy$), withLatestFrom(this.store.branches$))
      .subscribe(([query, branches]) =>
        query?.length > 1 ? this.filterBranchesFn({ query, branches }) : this.store.setFilteredBranches(branches),
      );
  }

  filterBranchesFn({ query, branches }: { query: string; branches: BranchDTO[] }) {
    let filteredBranches = branches?.filter((branch) => this.filterFn({ query, branch }));
    if (filteredBranches?.length > 0) {
      this.store.setFilteredBranches(filteredBranches);
      return;
    }

    if (query.length === 5 && !isNaN(Number(query))) {
      this.store.perimeterSearch();
      return;
    }

    this.store.setFilteredBranches([]);
  }

  filterFn = ({ query, branch }: { query: string; branch: BranchDTO }): boolean =>
    branch?.name?.toLowerCase()?.indexOf(query.toLowerCase()) >= 0 ||
    branch?.key?.toLowerCase()?.indexOf(query.toLowerCase()) >= 0 ||
    branch?.address?.city?.toLowerCase()?.indexOf(query.toLowerCase()) >= 0 ||
    branch?.address?.zipCode?.indexOf(query) >= 0;

  onQueryChange(query: string) {
    if (query.trim().length === 0) {
      return;
    }
    this.store.setQuery(query);
    this.complete.next(query.trim());
  }

  openComplete() {
    this.autocompleteComponent?.open();
    this.store.setQuery('');
    this.complete.next('');
  }

  setBranch(branch?: BranchDTO) {
    if (this.value !== branch) {
      this._value = branch;
      this.store.setSelectedBranch(branch);
      this.emitValues(branch);
    }
    this.closeAutocomplete();
  }

  clear() {
    this.setBranch();
    this.complete.next('');
    this.onChange(undefined);
    this.onTouched();
    this.valueChange.emit(undefined);
  }

  emitValues(branch?: BranchDTO) {
    if (branch) {
      this.onChange(branch);
      this.onTouched();
      this.valueChange.emit(branch);
    }
  }

  closeAndClearAutocomplete() {
    this.autocompleteComponent?.close();
    this.complete.next('');
  }

  focus() {
    asapScheduler.schedule(() => {
      this.branchInput?.nativeElement?.focus();
      this.openComplete();
      this.focused = true;
    }, 1);
  }

  @HostListener('focusout', ['$event'])
  closeAutocomplete(event?: FocusEvent) {
    asapScheduler.schedule(() => {
      this.focused = false;
    }, 1);

    // Soll bei Klick auf den Branch-Selector und auf die Scrollbar das Autocomplete nicht schließen
    const isBranchSelector =
      (event?.target as HTMLElement)?.classList.contains('shared-branch-selector') ||
      (event?.target as HTMLElement)?.classList.contains('shared-branch-selector-input');
    // Schließe Autocomplete bei Klick außerhalb der Komponente
    const outside = !(
      (event?.relatedTarget as HTMLElement)?.classList.contains('shared-branch-selector') ||
      (event?.relatedTarget as HTMLElement)?.classList.contains('shared-branch-selector-autocomplete-option')
    );
    // Fix ExpressionAfterHasBeenChecked Error if clicking on clear Icon in ControlValueAccessor Context
    const isClose = (event?.target as HTMLElement)?.classList.contains('shared-branch-selector-clear-input-icon');
    if ((!isBranchSelector || outside) && !isClose) {
      this.store.setQuery(this.store.formatBranch(this.value));
      this.closeAndClearAutocomplete();
      // Fix um nach der Auswahl einer Filiale den Focus auf dem ipad zu verlieren
      this._elementRef?.nativeElement?.blur();
    }
  }

  @HostListener('window:click', ['$event'])
  outsideClick(event?: MouseEvent) {
    if (
      this.focused &&
      !(this._elementRef.nativeElement.contains(event?.target) || this._elementRef.nativeElement === event?.target)
    ) {
      this.onClose(event);
    }
  }

  onClose(event?: MouseEvent) {
    this.focused = false;
    if (this.autocompleteComponent?.opend) {
      event?.preventDefault();
      event?.stopImmediatePropagation();
      this.store.setQuery(this.store.formatBranch(this.value));
      this.closeAndClearAutocomplete();
      this._elementRef?.nativeElement?.blur();
    }
  }

  onKeyup(event: KeyboardEvent) {
    if (event.key === 'Enter') {
      this.handleEnterEvent();
    } else if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
      this.handleArrowUpDownEvent(event);
    }
  }

  handleEnterEvent() {
    if (this.autocompleteComponent?.opend && this.autocompleteComponent?.activeItem) {
      this.setBranch(this.autocompleteComponent?.activeItem?.item);
    }
  }

  handleArrowUpDownEvent(event: KeyboardEvent) {
    this.autocompleteComponent?.handleKeyboardEvent(event);
    if (this.autocompleteComponent?.activeItem) {
      this.store.setQuery(this.store.formatBranch(this.autocompleteComponent.activeItem.item));
    }
  }

  @HostBinding('class.shared-branch-selector-opend') get class() {
    return !!this.autocompleteComponent?.opend;
  }
}
