import { QueryTokenDTO } from '@swagger/oms';
import { Subject } from 'rxjs';
import { IUiInputGroup, UiInputGroup } from './ui-input-group';
import { IUiOrderBy, UiOrderBy } from './ui-order-by';

export interface IUiFilter {
  filter?: Array<IUiInputGroup>;
  input?: Array<IUiInputGroup>;
  orderBy?: Array<IUiOrderBy>;
}

export class UiFilter implements IUiFilter {
  //#region implements IUiFilterQuerySettingsDTO
  private _filter?: Array<UiInputGroup>;
  get filter() {
    return this._filter;
  }

  private _input?: Array<UiInputGroup>;
  get input() {
    return this._input;
  }

  private _orderBy?: Array<UiOrderBy>;
  get orderBy() {
    return this._orderBy;
  }
  //#endregion

  readonly changes = new Subject<{ keys: (keyof IUiFilter)[]; orderBy: UiFilter }>();

  getQueryToken(): QueryTokenDTO {
    const token: QueryTokenDTO = {
      input: this.getInputAsStringDictionary(),
      filter: this.getFilterAsStringDictionary(),
      orderBy: this.orderBy?.filter((ob) => ob.selected).map((ob) => ob.toObject()),
    };

    return token;
  }

  getQueryParams(): Record<string, string> {
    const params: Record<string, string> = {};

    this.input?.forEach((inputGroup) => {
      const group = inputGroup.group;
      inputGroup.input.forEach((input) => {
        const key = input.key;
        params[`${group}_${key}`] = input.toStringValue();
      });
    });

    this.filter?.forEach((inputGroup) => {
      const group = inputGroup.group;
      inputGroup.input.forEach((input) => {
        const key = input.key;

        params[`${group}_${key}`] = input.toStringValue();
      });
    });

    this.orderBy
      ?.filter((ob) => ob.selected)
      .forEach((ob) => {
        params[`order_by_${ob.by}`] = ob?.desc ? 'desc' : 'asc';
      });

    return params;
  }

  fromQueryParams(params: Record<string, string>) {
    const groupKeys = Object.keys(params);

    groupKeys.forEach((gk) => {
      const group = gk.split('_')[0];
      const key = gk.substr(group.length + 1);

      this.input
        ?.filter((ig) => ig.group === group)
        .forEach((inputGroup) => {
          inputGroup.input
            .filter((i) => i.key === key)
            .forEach((input) => {
              input.fromStringValue(key, params[gk]);
            });
        });

      this.filter
        ?.filter((ig) => ig.group === group)
        .forEach((inputGroup) => {
          inputGroup.input
            .filter((i) => i.key === key)
            .forEach((input) => {
              input.fromStringValue(key, params[gk]);
            });
        });
    });

    this.orderBy.forEach((ob) => {
      const order = params[`order_by_${ob.by}`];
      const selected = (order && ob.desc && order === 'desc') || (!ob.desc && order === 'asc');
      ob.setSelected(selected);
    });
  }

  getInputAsStringDictionary(): Record<string, string> {
    const input: Record<string, string> = {};

    this.input?.forEach((ig) => {
      const group = ig.group;
      const inputWithValue = ig.input.find((i) => i.toStringValue());
      if (!inputWithValue) {
        return;
      }

      const inputsWithTheSameGroup = this.filter?.find((f) => f.group === group);
      const inputsWithTargetInput = inputsWithTheSameGroup?.input?.filter((ig) => ig.selected && ig.target === 'input');

      if (inputsWithTargetInput?.length) {
        inputsWithTargetInput.forEach((ifk) => {
          input[ifk.key] = inputWithValue.toStringValue();
        });
      } else {
        input[inputWithValue.key] = inputWithValue.value;
      }
    });

    return input;
  }

  getFilterAsStringDictionary(): Record<string, string> {
    const filter: Record<string, string> = {};
    for (const inputGroup of this.filter) {
      for (const input of inputGroup.input.filter((i) => i.target === 'filter')) {
        const key = input.key;
        const value = input.toStringValue();

        if (!!value) {
          filter[key] = value;
        }
      }
    }
    return filter;
  }

  toObject(): IUiFilter {
    return {
      filter: this.filter?.map((filter) => filter.toObject()),
      input: this.input?.map((input) => input.toObject()),
      orderBy: this.orderBy?.map((orderBy) => orderBy.toObject()),
    };
  }

  static create(settings: IUiFilter) {
    const target = new UiFilter();

    target._filter = settings?.filter?.map((filter) => UiInputGroup.create(filter, target)) || [];
    target._input = settings?.input?.map((input) => UiInputGroup.create(input, target)) || [];
    target._orderBy = settings?.orderBy?.map((orderBy) => UiOrderBy.create(orderBy, target)) || [];

    return target;
  }

  static getQueryParamsFromQueryTokenDTO(queryToken: QueryTokenDTO): Record<string, string> {
    const queryParams: Record<string, string> = {};

    if (queryToken.input?.qs) {
      queryParams['main_qs'] = queryToken.input.qs;
    }

    return queryParams;
  }
}
