import {
  Component,
  ChangeDetectionStrategy,
  Input,
  Output,
  EventEmitter,
  ElementRef,
  ViewChild,
  OnChanges,
  SimpleChanges,
  ChangeDetectorRef,
  AfterViewInit,
  OnDestroy,
} from '@angular/core';
import { SelectFilterOption } from '../../../models';
import { BehaviorSubject, interval, Subscription } from 'rxjs';
import { cloneSelectFilterOption, flattenSelectFilterOption } from '../../../utils';

@Component({
  selector: 'app-select-filter',
  templateUrl: 'select-filter.component.html',
  styleUrls: ['select-filter.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SelectFilterComponent implements OnChanges, AfterViewInit, OnDestroy {
  @Input()
  options: SelectFilterOption[];

  @Output()
  optionsChanged = new EventEmitter<SelectFilterOption[]>();

  @Input()
  max?: number;

  @Input() module: 'Customer' | 'Branch' = 'Branch';

  @ViewChild('optionsContainer')
  optionsContainer: ElementRef;

  canScroll = false;
  scrollPositionPersantage = 0;
  allSelected$ = new BehaviorSubject<boolean>(false);

  updateScrollPositionPersantageIntervalSub: Subscription;

  constructor(private cdr: ChangeDetectorRef) {}

  ngOnDestroy(): void {
    if (this.updateScrollPositionPersantageIntervalSub) {
      this.updateScrollPositionPersantageIntervalSub.unsubscribe();
    }
  }

  ngAfterViewInit(): void {
    this.updateScrollButton();
    this.updateScrollPositionPersantageIntervalSub = interval(500).subscribe(() => this.updateScrollButton());
  }

  updateScrollButton() {
    this.updateScrollPositionPersantage();
    this.setCanScroll();
    this.cdr.markForCheck();
  }

  setCanScroll() {
    if (this.optionsContainer) {
      const container = this.optionsContainer.nativeElement;
      const scrollHeight = container.scrollHeight;
      const height = container.clientHeight;

      if (this.canScroll !== scrollHeight > height) {
        this.canScroll = scrollHeight > height;
        this.cdr.markForCheck();
      }
    }
  }

  ngOnChanges({ options }: SimpleChanges): void {
    if (options) {
      this.options = this.options.map(cloneSelectFilterOption);
      this.setAllSelected(options.currentValue);
      this.cdr.markForCheck();
    }
  }

  createSelctFilterOptionContext(option: SelectFilterOption) {
    return { $implicit: option };
  }

  selectAll() {
    const selectFn = (option: SelectFilterOption): SelectFilterOption => {
      return {
        ...option,
        selected: !this.allSelected$.value,
        options: Array.isArray(option.options) ? option.options.map(selectFn) : option.options,
      };
    };

    this.options = this.options.map(cloneSelectFilterOption).map(selectFn);

    this.optionsChanged.emit(this.options);
    this.setAllSelected(this.options);
    this.cdr.markForCheck();
  }

  optionChanged(option: SelectFilterOption, index: number) {
    let options = [...this.options];
    options[index] = option;
    const selectedOptions = flattenSelectFilterOption(options).filter((o) => o.selected);

    if (this.max > 0 && selectedOptions.length > this.max) {
      const unselectOptions = selectedOptions
        .filter((o) => !(o.key === option.key && o.name === option.name))
        .slice(0, selectedOptions.length - this.max);

      const updateOption = (o: SelectFilterOption): SelectFilterOption =>
        unselectOptions.some((s) => s.key === o.key && s.name === o.name) ? { ...o, selected: false } : o;
      options = options.map(updateOption);
    }

    this.options = options;
    this.optionsChanged.emit(this.options);
    this.setAllSelected(this.options);
    this.cdr.markForCheck();
  }

  setAllSelected(options: SelectFilterOption[]) {
    this.allSelected$.next(this.isAllSelected(options));
  }

  private isAllSelected(options: SelectFilterOption[]): boolean {
    if (options.every((option) => !!option.selected)) {
      return true;
    }

    return options.every((option) => {
      if (!option.options || !option.options.length) {
        return option.selected;
      }

      return option.options.every((o) => o.selected);
    });
  }

  updateScrollPositionPersantage() {
    if (this.optionsContainer) {
      const container: HTMLElement = this.optionsContainer.nativeElement;
      const scrollHeight = container.scrollHeight;
      const scrollTop = container.scrollTop;
      const height = container.clientHeight;

      const scrollPositionPersantage = Math.round((100 / (scrollHeight - height)) * scrollTop);
      if (this.scrollPositionPersantage !== scrollPositionPersantage) {
        this.scrollPositionPersantage = scrollPositionPersantage;
        this.cdr.markForCheck();
      }
    }
  }

  scroll() {
    const ele: HTMLElement = this.optionsContainer.nativeElement;
    if (this.scrollPositionPersantage >= 20) {
      ele.scrollTo({ top: 0, behavior: 'smooth' });
    } else {
      ele.scrollTo({ top: ele.scrollHeight, behavior: 'smooth' });
    }
  }
}
