import { Component, OnInit, Input, Output, EventEmitter, OnDestroy, ChangeDetectorRef } from '@angular/core';
import { Filter, FilterOption } from '@isa/remission';
import { fadeInAnimation } from '../../animations/fadeIn.animation';
import { growShrinkAnimation } from '../../animations/grow-shrink.animation';
import { RemissionResourceType } from '../../models/remission-resource-type.model';
import { Store } from '@ngxs/store';
import { RemissionSelectors } from 'apps/sales/src/app/core/store/selectors/remission.selectors';
import { Subject } from 'rxjs';
import { isNullOrUndefined } from 'util';
import { filter, take, takeUntil } from 'rxjs/operators';
import { RemissionHelperService } from '../../services/remission-helper.service';
import isEqual from 'lodash/isEqual';

@Component({
  selector: 'app-remission-filter-item',
  templateUrl: './remission-filter-item.component.html',
  styleUrls: ['./remission-filter-item.component.scss'],
  animations: [fadeInAnimation, growShrinkAnimation],
})
export class RemissionFilterItemComponent implements OnInit, OnDestroy {
  @Input() filter: Filter;
  @Input()
  get remissionResourceType(): RemissionResourceType {
    return this._remissionResourceType;
  }
  set remissionResourceType(value: RemissionResourceType) {
    this._remissionResourceType = value;
  }
  @Output() closeOtherFilters = new EventEmitter<string>();
  @Output() toggleChange = new EventEmitter();

  destroy$ = new Subject();
  expanded = false;
  hasGrouping: boolean;
  selectedGroups: FilterOption[] = [];
  expandedGroups: FilterOption[] = [];
  selectedOptions: FilterOption[] = [];
  hideSubFilters = false;
  updateInactive = false;

  private previousSelectedGroups: FilterOption[] = [];
  private previousSelectedOptions: FilterOption[] = [];
  private _remissionResourceType: RemissionResourceType;

  constructor(private store: Store, private remissionHelper: RemissionHelperService, private cdrf: ChangeDetectorRef) {}

  ngOnInit() {
    this.areFilterOptionsGoups(this.filter);
    this.subscriptions();
    if (this.remissionResourceType === RemissionResourceType.Overflow) {
      this.hideSubFilters = true;
    }
  }

  ngOnDestroy() {
    this.destroy$.next();
  }

  subscriptions() {
    this.store
      .select(RemissionSelectors.getRemissionSelectedOptions)
      .pipe(
        filter((data) => !isNullOrUndefined(data)),
        take(1),
        takeUntil(this.destroy$)
      )
      .subscribe((filterOptions) => {
        const flatFilterOptions = [];
        this.filter.options.forEach(({ options }) => flatFilterOptions.push(...options));
        this.selectedOptions = flatFilterOptions.filter((item) => filterOptions.includes(item.id));

        this.selectedGroups = this.filter.options.filter((groupOptions) => {
          return groupOptions.options === undefined
            ? filterOptions.includes(groupOptions.id)
            : groupOptions.options.every((option) => filterOptions.includes(option.id));
        });
        this.checkUpdateInactive();
      });

    this.remissionHelper.filteOptionsDeleteEvents$
      .pipe(
        filter((data) => !isNullOrUndefined(data)),
        filter(
          (data) =>
            this.selectedGroups.some((group) => group.name === data.name) ||
            this.selectedOptions.some((option) => option.name === data.name)
        ),
        takeUntil(this.destroy$)
      )
      .subscribe((option) => {
        if (this.remissionResourceType === RemissionResourceType.Overflow) {
          this.toggleFirstLevelItem(option);
        } else {
          if (option.options) {
            this.toggleGrouping(option);
          } else {
            this.removeOptionsFromSelected(option);
          }
          if (this.hasGrouping) {
            this.uncheckGroupOfOption(option);
          }
        }
        this.updateFilters();
      });

    this.remissionHelper.filtersClearedEvent$.pipe(takeUntil(this.destroy$)).subscribe(() => {
      this.selectedOptions = [];
      this.selectedGroups = [];
      this.checkUpdateInactive();
      this.updateFilters();
    });

    this.remissionHelper.reApplyFilters$
      .pipe(
        filter((data) => !isNullOrUndefined(data) && data === this.remissionResourceType),
        takeUntil(this.destroy$)
      )
      .subscribe(() => {
        this.updateFilters();
      });

    this.remissionHelper.closeFilters$.pipe(takeUntil(this.destroy$)).subscribe(() => {
      this.updateFilters();
    });
  }

  updateFilters() {
    this.expanded = false;
    const storeFilters = this.store.selectSnapshot(RemissionSelectors.getRemissionFilters);
    const { filter } = this.store.selectSnapshot(RemissionSelectors.getRemissionProcess);
    const newFilters = {
      ...storeFilters,
      [filter.source]: {
        ...storeFilters[filter.source],
        [filter.target.name]: {
          ...storeFilters[filter.source][filter.target.name],
          [this.filter.id]:
            filter.source === RemissionResourceType.Overflow
              ? this.selectedGroups.map((item) => item.id)
              : this.selectedOptions.map((item) => item.id),
        },
      },
    };
    this.remissionHelper.updateFilters({
      filter: newFilters,
    });
    this.remissionHelper.setFilterHeight(0);
  }

  uncheckGroupOfOption(option: FilterOption) {
    const groups = this.filter.options;
    groups.forEach((gr) => {
      const containsOption = gr.options ? gr.options.findIndex((op) => op.id === option.id) !== -1 : gr.id === option.id;
      if (containsOption) {
        const index = this.selectedGroups.findIndex((i) => i.name === gr.name);
        if (index > -1) {
          this.selectedGroups.splice(index, 1);
        }
      }
    });
  }

  noFiltersSelected = () => (this.selectedGroups.length > 0 ? false : this.selectedOptions.length < 1);

  areFilterOptionsGoups(f: Filter) {
    this.hasGrouping = f.options && f.options.length > 0 ? this.isInstanceOfFilterOpionsGroups(f.options) : false;
  }

  isInstanceOfFilterOpionsGroups(object: any[]): object is FilterOption[] {
    return 'options' in object[0];
  }

  addGroupToSelected(group: FilterOption) {
    this.selectedGroups.push(group);
    this.selectAllOptionsOfGroup(group);
  }

  removeGroupFromSelected(group: FilterOption) {
    const index = this.selectedGroups.findIndex((t) => t.name === group.name);
    if (index > -1) {
      this.selectedGroups.splice(index, 1);
    }
    this.unSelectAllOptionsOfGroup(group);
  }

  isGroupSelected(group: FilterOption) {
    const index = this.selectedGroups.findIndex((t) => t.name === group.name);
    return index > -1;
  }

  addGroupToExpanded(group: FilterOption) {
    this.expandedGroups.push(group);
  }

  removeGroupFromExpanded(group: FilterOption) {
    const index = this.expandedGroups.findIndex((i) => i.name === group.name);
    if (index > -1) {
      this.expandedGroups.splice(index, 1);
    }
  }

  isGroupExpanded(group: FilterOption) {
    const index = this.expandedGroups.findIndex((i) => i.name === group.name);
    return index > -1;
  }

  addOptionToSelected(option: FilterOption) {
    this.selectedOptions.push(option);
  }

  removeOptionsFromSelected(options: FilterOption) {
    const index = this.selectedOptions.findIndex((o) => o.id === options.id);
    if (index > -1) {
      this.selectedOptions.splice(index, 1);
    }
  }

  isOptionsSelected(option: FilterOption) {
    const index = this.selectedOptions.findIndex((op) => op.id === option.id);
    return index > -1;
  }

  isFirstLevelItemSelected(val: FilterOption) {
    if (this.hasGrouping) {
      return this.isGroupSelected(val);
    } else {
      return this.isOptionsSelected(val);
    }
  }

  toggleFirstLevelItem(val: FilterOption) {
    if (this.hasGrouping) {
      if (this.remissionResourceType === RemissionResourceType.Overflow) {
        const isGroupSelected = this.isFirstLevelItemSelected(val);
        if (!isGroupSelected) {
          this.selectedGroups.forEach((gr) => {
            this.removeGroupFromSelected(gr);
          });
        }
      }
      this.toggleGrouping(val);
    } else {
      this.toggleOption(val);
    }
  }

  toggleGrouping(group: FilterOption) {
    const isGroupSelected = this.isGroupSelected(group);
    if (isGroupSelected) {
      this.removeGroupFromSelected(group);
    } else {
      this.remissionResourceType === RemissionResourceType.Overflow ? this.selectedGroups.push(group) : this.addGroupToSelected(group);
    }
    this.checkUpdateInactive();
  }

  toggleExpandedGrouping(group: FilterOption) {
    const isGroupExpanded = this.isGroupExpanded(group);
    if (isGroupExpanded) {
      this.removeGroupFromExpanded(group);
    } else {
      this.addGroupToExpanded(group);
    }
  }

  toggleOption(option: FilterOption, modifyGroup: boolean = false, group: FilterOption = null) {
    const isOptionSelected = this.isOptionsSelected(option);
    if (isOptionSelected) {
      this.removeOptionsFromSelected(option);
      modifyGroup.ifTrue(() => {
        this.unSelectGroupOfOptions(group);
      });
    } else {
      (this.remissionResourceType === RemissionResourceType.Overflow).ifTrue(() => {
        this.selectedOptions = [];
        this.checkUpdateInactive();
      });
      this.addOptionToSelected(option);
      modifyGroup.ifTrue(() => {
        this.selectGroupOfOptions(group);
      });
    }
    this.checkUpdateInactive();
  }

  unSelectGroupOfOptions(group: FilterOption) {
    const index = this.selectedGroups.findIndex((i) => i.name === group.name);
    if (index > -1) {
      this.selectedGroups.splice(index, 1);
    }
  }

  selectGroupOfOptions(group: FilterOption) {
    const filterGroup = this.filter.options.find((t) => t.name === group.name);
    const options = filterGroup.options;
    let allSelected = true;
    options.forEach((op) => {
      const isSelected = this.selectedOptions.findIndex((o) => o.id === op.id) !== -1;
      if (!isSelected) {
        allSelected = false;
      }
    });
    allSelected.ifTrue(() => {
      this.addGroupToSelected(group);
    });
  }

  selectAllOptionsOfGroup(group: FilterOption) {
    const filterGroup = this.filter.options.find((t) => t.name === group.name);
    const options = filterGroup && filterGroup.options;
    if (options) {
      options.forEach((op) => {
        const isSelected = this.selectedOptions.findIndex((o) => o.id === op.id) !== -1;
        if (!isSelected) {
          this.selectedOptions.push(op);
        }
      });
    }
  }

  unSelectAllOptionsOfGroup(group: FilterOption) {
    const filterGroup = this.filter.options.find((t) => t.name === group.name);
    const options = filterGroup && filterGroup.options;
    if (options) {
      options.forEach((op) => {
        const isSelected = this.selectedOptions.findIndex((o) => o.id === op.id) !== -1;
        if (isSelected) {
          this.removeOptionsFromSelected(op);
        }
      });
    }
  }

  toggle() {
    if (
      this.expanded &&
      (!isEqual(this.selectedOptions, this.previousSelectedOptions) || !isEqual(this.selectedGroups, this.previousSelectedGroups))
    ) {
      this.updateFilters();
    } else {
      this.expanded = !this.expanded;
      this.previousSelectedGroups = [...this.selectedGroups];
      this.previousSelectedOptions = [...this.selectedOptions];
      if (this.expanded) {
        this.closeOtherFilters.emit(this.filter.id);
      }
    }
    this.toggleChange.emit(this.expanded);
    this.setFilterHeight(this.expanded);
  }

  setFilterHeight(expanded: boolean) {
    this.cdrf.detectChanges();
    setTimeout(() => {
      if (expanded) {
        const options = document.getElementById('options');
        if (options) {
          const height = options.getBoundingClientRect().height;
          this.remissionHelper.setFilterHeight(height);
        } else {
          this.remissionHelper.setFilterHeight(0);
        }
      } else {
        this.remissionHelper.setFilterHeight(0);
      }
    });
  }

  public closeNotCurrentFilters(id: string) {
    if (this.filter.id !== id) {
      this.updateFilters();
    }
  }

  private checkUpdateInactive() {
    this.updateInactive = this.selectedGroups.length === 0 && this.selectedOptions.length === 0;
  }
}
