import { Injectable } from '@angular/core';
import { Action, Store } from '@ngrx/store';
import { Actions, createEffect, ofType, OnInitEffects } from '@ngrx/effects';
import { Actions as NgxsActions, ofActionDispatched } from '@ngxs/store';
import { AddProcess, DeleteProcess } from 'apps/sales/src/app/core/store/actions/process.actions';
import * as actions from './search.actions';
import { switchMap, withLatestFrom, catchError, map, flatMap } from 'rxjs/operators';
import {
  StrictHttpResponse,
  ListResponseArgsOfOrderItemListItemDTO,
  ResponseArgsOfIEnumerableOfInputDTO,
  AbholfachService,
} from '@swagger/oms';
import { SearchStateFacade } from './search.facade';
import { of, NEVER } from 'rxjs';
import { inputDtoToSelectedFilters, inputDtoToPrimaryFilterOption } from './mappers';
import { SelectFilter } from 'apps/sales/src/app/modules/filter';
import { PrimaryFilterOption } from 'apps/sales/src/app/modules/shelf/defs';
import { isNullOrUndefined, isNumber } from 'util';

@Injectable()
export class SearchEffects implements OnInitEffects {
  constructor(
    private actions$: Actions,
    private ngxsActions$: NgxsActions,
    private store: Store<any>,
    private searchStateFacade: SearchStateFacade,
    private abholfach: AbholfachService
  ) {
    this.initAddProcess();
    this.initRemoveProcess();
  }

  reloadResults$ = createEffect(() =>
    this.actions$.pipe(
      ofType(actions.reloadResults),
      map((a) => actions.fetchResult({ id: a.id, skip: 0 }))
    )
  );

  fetchResults$ = createEffect(() =>
    this.actions$.pipe(
      ofType(actions.fetchResult),
      switchMap((a) =>
        of(a).pipe(
          withLatestFrom(this.searchStateFacade.getProcess$(a.id), this.searchStateFacade.selectedFilter$),
          flatMap(([_, process, filter]) =>
            this.abholfach
              .AbholfachWarenausgabeResponse({
                input: {
                  qs: process.input,
                },
                filter: (filter as unknown) as {
                  [key: string]: string;
                },
                skip: isNumber(a.skip) ? a.skip : process.result.length || 0,
                take: isNumber(a.take) ? a.take : 20,
              })
              .pipe(
                catchError((err) => of<StrictHttpResponse<ListResponseArgsOfOrderItemListItemDTO>>(err)),
                map((response) => actions.fetchResultDone({ id: a.id, response }))
              )
          )
        )
      )
    )
  );

  fetchResultDone$ = createEffect(() =>
    this.actions$.pipe(
      ofType(actions.fetchResultDone),
      flatMap((action) => {
        if (action.response.ok) {
          const result = action.response.body;
          const shouldSetTimestamp = !!result.skip;
          return [
            ...(shouldSetTimestamp ? [actions.setTimestamp({ id: action.id })] : []),
            actions.setHits({ id: action.id, hits: result.hits || 0 }),
            actions.addResult({ id: action.id, result: result.result || [] }),
          ];
        }
        return NEVER;
      })
    )
  );

  clearResult$ = createEffect(() => {
    return this.actions$.pipe(
      ofType(actions.resetPrimaryFilters, actions.resetSelectFilters, actions.setSelectFilters, actions.setInput),
      map((action) => actions.clearResults({ id: action.id }))
    );
  });

  fetchFilters$ = createEffect(() =>
    this.actions$.pipe(
      ofType(actions.fetchFilters),
      flatMap((action) =>
        this.abholfach.AbholfachGetWarenausgabeFilterResponse().pipe(
          catchError((err) => of<StrictHttpResponse<ResponseArgsOfIEnumerableOfInputDTO>>(err)),
          map((response) => actions.fetchFiltersDone({ id: action.id, response }))
        )
      )
    )
  );

  fetchFiltersDone$ = createEffect(() =>
    this.actions$.pipe(
      ofType(actions.fetchFiltersDone),
      flatMap((action) => {
        if (action.response.ok) {
          const result = action.response.body;

          const selectedFiltersRaw = result.result.filter((filter) => !!filter.options);
          const primaryFiltersRaw = result.result.filter((filter) => isNullOrUndefined(filter.options));

          const selectFilters: SelectFilter[] = selectedFiltersRaw.map((input) => inputDtoToSelectedFilters(input));

          const primaryFilters: PrimaryFilterOption[] = primaryFiltersRaw.map((input) => inputDtoToPrimaryFilterOption(input));

          return [
            actions.setSelectFilters({
              id: action.id,
              filters: selectFilters,
            }),
            actions.setPrimaryFilters({
              id: action.id,
              filters: primaryFilters,
            }),
          ];
        }

        return NEVER;
      })
    )
  );

  initAddProcess() {
    this.ngxsActions$.pipe(ofActionDispatched(AddProcess)).subscribe((action) => {
      if (action instanceof AddProcess) {
        this.store.dispatch(actions.addSearchProcess({ id: action.payload.id }));

        this.searchStateFacade.fetchFilters(action.payload.id);
      }
    });
  }

  ngrxOnInitEffects(): Action {
    return actions.cleanup();
  }

  initRemoveProcess() {
    this.ngxsActions$.pipe(ofActionDispatched(DeleteProcess)).subscribe((action: DeleteProcess) => {
      this.store.dispatch(actions.removeSearchProcess({ id: action.payload.id }));
    });
  }
}
