import { InputDTO, OptionDTO, InputType } from '@swagger/cat';
import { SelectFilter, SelectFilterOption, FilterType, Filter, FilterOption, RangeFilter } from '@ui/filter';
import { RangeFilterOption } from 'apps/ui/filter/src/lib/models/range-filter-option';
import { isRangeFilterOption, isSelectFilterOption } from 'apps/ui/filter/src/lib/type-guards';
import { cloneDeep } from 'lodash';

export function fromInputDto(input: InputDTO): Filter {
  if (inputTypeToType(input.type) === 'integer-range') {
    return {
      key: input.key,
      name: input.label,
      description: input.description,
      type: inputTypeToType(input.type),
      target: input.target,
      options: input.options && input.options.values.map(fromOptionDtoRange),
    } as RangeFilter;
  }

  if (inputTypeToType(input.type) !== 'integer-range') {
    return {
      key: input.key,
      name: input.label,
      max: input.options && input.options.max,
      type: inputTypeToType(input.type),
      target: input.target,
      options: input.options && input.options.values.map((v) => fromOptionDto(v)),
    } as SelectFilter;
  }
}

export function fromOptionDto(option: OptionDTO): SelectFilterOption {
  return {
    type: 'select',
    name: option.label,
    id: option.key || option.value,
    selected: option.selected || false,
    initial_selected_state: option.selected || false,
    expanded: false,
    options: option.values && option.values.map((v) => fromOptionDto(v)),
  } as SelectFilterOption;
}

export function fromOptionDtoRange(option: OptionDTO): RangeFilterOption {
  return {
    type: 'integer-range',
    name: option.label,
    selected: option.selected || false,
    value: '',
    id: option.key || option.value,
    minValue: option.minValue,
    maxValue: option.maxValue,
    placeholder: option.placeholder,
  } as RangeFilterOption;
}

export function inputTypeToType(type: InputType): FilterType {
  switch (type) {
    case 2:
      return 'select';

    case 8:
      return 'input_selector';

    case 4096:
      return 'integer-range';

    default:
      return 'select';
  }
}

export function mapFilterArrayToStringDictionary(source: Filter[]): { [key: string]: string } {
  const dict: { [key: string]: string } = {};
  for (const filter of source) {
    const options: FilterOption[] = filter.options;
    const selected = options.filter((o) => o.selected);
    const selectedSubOptions: FilterOption[] = [];

    const optionsWithSuboptions: FilterOption[] = options.filter((o) => (isSelectFilterOption(o) ? o?.options?.length > 0 : null));
    for (const o of optionsWithSuboptions) {
      if (isSelectFilterOption(o)) {
        const subOptions = o.options.filter((sO) => sO.selected);
        if (subOptions?.length > 0) {
          subOptions.forEach((sO) => selectedSubOptions.push(sO));
        }
      }
    }

    const allSelectedOptions = [...selected, ...selectedSubOptions];

    if (allSelectedOptions.length > 0) {
      dict[filter.key] = allSelectedOptions
        .map((o: FilterOption) => {
          if (filter.key === 'reading-age') {
            if (o.id === 'start') {
              return `${(o as RangeFilterOption).value}-`;
            } else if (o.id === 'stop') {
              return `-${(o as RangeFilterOption).value}`;
            }
          } else {
            return o.id;
          }
        })
        .join(';');
    }
    if (allSelectedOptions.find((o) => o.id === 'start') && allSelectedOptions.find((o) => o.id === 'stop')) {
      dict['reading-age'] = dict['reading-age'].replace('-;', '');
    }
  }
  return dict;
}

export function mapSelectedFilterToParams(source: Filter[]): string {
  const dict: { [key: string]: string } = {};
  for (const filter of source) {
    const options: FilterOption[] = filter.options;

    const selected = options.filter((o) => o.type === 'select' && o.selected);
    const selectedSubOptions: FilterOption[] = [];
    const optionsWithSuboptions: FilterOption[] = options.filter((o) => (isSelectFilterOption(o) ? o?.options?.length > 0 : null));
    for (const o of optionsWithSuboptions) {
      if (isSelectFilterOption(o)) {
        const subOptions = o.options.filter((sO) => sO.type === 'select' && sO.selected);
        if (subOptions?.length > 0) {
          subOptions.forEach((sO) => selectedSubOptions.push(sO));
        }
      }
    }
    const allSelectedOptions = [...selected, ...selectedSubOptions];

    const integerRange = options.filter((o) => o.type === 'integer-range' && o.selected);
    if (integerRange.length > 0) {
      dict[filter.key] = integerRange
        .map((o) => {
          if (isRangeFilterOption(o)) {
            return o.id + '-' + o.value;
          }
        })
        .join(';');
    }
    if (allSelectedOptions?.length > 0) {
      dict[filter.key] = allSelectedOptions.map((o) => o.id).join(';');
    }
  }
  return [...Object.values(dict)].join(';');
}

export function mapExpandedFilterOptionsToParams(source: Filter[]): string {
  const expanded: string[] = [];
  for (const filter of source) {
    const options: FilterOption[] = filter.options;
    const expandedOptions = options.filter((o) => isSelectFilterOption(o) && o.expanded).map((o) => o.id);
    if (expandedOptions.length > 0) {
      expanded.push(...expandedOptions);
    }
  }

  if (expanded.length > 0) {
    return expanded.join(';');
  }
}

export function mapSelectedParamsToFilter(params: string, defaultFilters: Filter[], expanded?: string): Filter[] {
  const filters = cloneDeep(defaultFilters);
  const keys = params?.split(';');
  const expandedIds = expanded?.split(';');
  // Wenn selectedFilters == undefined dann soll der default Filter verwendet werden
  // ist der selectedFilter ein leerstring ist dann sind alle Filter nicht selected
  if (params != undefined) {
    filters?.forEach((filter) => {
      filter?.options?.forEach((option: FilterOption) => {
        if (isSelectFilterOption(option)) {
          option.expanded = expandedIds?.includes(String(option.id));
          option.selected = keys?.includes(String(option.id));
          if (option?.options?.length > 0) {
            option.options.forEach((subOption) => (subOption.selected = keys.includes(String(subOption.id))));
            option.selected = option.options.filter((subOption) => subOption.selected).length === option.options.length;
          }
        }
        if (isRangeFilterOption(option)) {
          const valueAndNumber = keys?.find((key) => key?.startsWith(String(option.id)))?.split('-');
          if (valueAndNumber) {
            option.selected = true;
            option.value = valueAndNumber[1];
          }
        }
      });
    });
  }
  return filters;
}
