import { EventEmitter } from '@angular/core';
import { Filter, RangeFilter, RangeFilterOption, SelectFilter, SelectFilterOption } from '../models';
import { isRangeFilter, isRangeFilterOption, isSelectFilter, isSelectFilterOption } from '../type-guards';

export abstract class FilterGroup {
  abstract value: Filter[];

  abstract valueChange: EventEmitter<Filter[]>;

  abstract active: Filter;

  updateView = new EventEmitter();

  selectAll(filter: Filter) {
    const current = this.getFilterRef(filter);
    current.options.forEach((option) => {
      option.selected = true;
      if (option?.options?.length > 0) {
        option.options.forEach((subOption) => (subOption.selected = true));
      }
    });
    this.valueChange.emit(this.value);
    this.updateView.emit();
  }

  unselectAll(filter: Filter) {
    const current = this.getFilterRef(filter);
    current.options.forEach((option) => {
      option.selected = false;
      if (option?.options?.length > 0) {
        option.options.forEach((subOption) => (subOption.selected = false));
      }
    });
    this.valueChange.emit(this.value);
    this.updateView.emit();
  }

  toggleOption(filter: SelectFilter, option: SelectFilterOption) {
    const optionRef = this.getSelectOptionRef(filter, option);

    if (isSelectFilterOption(optionRef.parentOption) && !optionRef.subOption) {
      optionRef.parentOption.selected = !optionRef.parentOption.selected;
      if (optionRef?.parentOption?.options?.length > 0) {
        optionRef.parentOption.options.forEach((subOption) => (subOption.selected = optionRef.parentOption.selected));
      }
    } else if (!!optionRef.subOption && isSelectFilterOption(optionRef.subOption)) {
      optionRef.subOption.selected = !optionRef.subOption.selected;
      const subOptionsCount = optionRef.parentOption.options.length;
      const selectedSubOptionsCount = optionRef.parentOption.options.filter((subOption) => subOption.selected).length;

      if (subOptionsCount === selectedSubOptionsCount) {
        optionRef.parentOption.selected = true;
      } else if (selectedSubOptionsCount >= 1) {
        // TODO: Zwischensymbol anzeigen
        optionRef.parentOption.selected = undefined;
      } else {
        optionRef.parentOption.selected = false;
      }
    }
    this.valueChange.emit(this.value);
    this.updateView.emit();
  }

  expandOption(filter: SelectFilter, option: SelectFilterOption, expand: boolean) {
    const optionRef = this.getSelectOptionRef(filter, option);
    optionRef.parentOption.expanded = expand;
    this.valueChange.emit(this.value);
    this.updateView.emit();
  }

  setRangeOption(filter: RangeFilter, option: RangeFilterOption) {
    const optionRef = this.getRangeOptionRef(filter, option);
    if (isRangeFilterOption(optionRef)) {
      optionRef.selected = option.selected;
      optionRef.value = option.value;
      this.valueChange.emit(this.value);
      this.updateView.emit();
    }
  }

  getFilterRef(filter: Filter): Filter {
    return this.value.find((f) => f.key === filter.key);
  }

  getRangeFilterRef(filter: Filter): RangeFilter {
    if (isRangeFilter(filter)) {
      return (this.value as RangeFilter[]).find((f) => f.key === filter.key);
    }
  }

  getSelectFilterRef(filter: Filter): SelectFilter {
    if (isSelectFilter(filter)) {
      return (this.value as SelectFilter[]).find((f) => f.key === filter.key);
    }
  }

  getRangeOptionRef(filter: RangeFilter, option: RangeFilterOption): RangeFilterOption {
    const filterRef = this.getRangeFilterRef(filter);
    if (isRangeFilter(filterRef)) {
      const options: RangeFilterOption[] = filterRef.options;
      if (filterRef) {
        const currentOption = options.find((o) => o.id === option.id);
        if (currentOption) {
          return currentOption;
        }
      }
    }
  }

  getSelectOptionRef(
    filter: SelectFilter,
    option: SelectFilterOption
  ): { subOption: SelectFilterOption; parentOption: SelectFilterOption } {
    const filterRef = this.getSelectFilterRef(filter);
    if (isSelectFilter(filterRef)) {
      const options: SelectFilterOption[] = filterRef.options;
      if (filterRef) {
        const currentOption = options.find((o) => o.id === option.id);
        if (currentOption) {
          return { subOption: null, parentOption: currentOption };
        } else {
          return this.getSubOptionRef(filter, option);
        }
      }
    }
  }

  getSubOptionRef(filter: SelectFilter, option: SelectFilterOption): { subOption: SelectFilterOption; parentOption: SelectFilterOption } {
    let subOption: SelectFilterOption;
    let parentOption: SelectFilterOption;
    const filterRef = this.getSelectFilterRef(filter);
    if (isSelectFilter(filterRef)) {
      const options: SelectFilterOption[] = filterRef.options;
      for (const o of options) {
        if (o.options && o.options.length > 0) {
          subOption = o.options.find((sO) => sO === option);
          if (subOption) {
            parentOption = o;
            break;
          }
        }
      }
    }
    return { subOption, parentOption };
  }
}
