import { HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { DomainGoodsService } from '@domain/oms';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { ListResponseArgsOfOrderItemListItemDTO, OrderItemListItemDTO, QuerySettingsDTO } from '@swagger/oms';
import { UiFilter } from '@ui/filter';
import { isResponseArgs } from '@utils/object';
import { Subject } from 'rxjs';
import { filter, mergeMap, switchMap, take, tap, withLatestFrom } from 'rxjs/operators';

export interface GoodsInSearchState {
  defaultSettings?: QuerySettingsDTO;
  queryParams?: Record<string, string>;
  filter?: UiFilter;
  message?: string;
  fetching: boolean;
  hits: number;
  results: OrderItemListItemDTO[];
  searchOptions?: { take?: number; skip?: number };
}

@Injectable()
export class GoodsInSearchStore extends ComponentStore<GoodsInSearchState> {
  get defaultSettings() {
    return this.get((s) => s.defaultSettings);
  }

  readonly defaultSettings$ = this.select((s) => s.defaultSettings);

  get filter() {
    return this.get((s) => s.filter);
  }

  readonly filter$ = this.select((s) => s.filter);

  get results() {
    return this.get((s) => s.results);
  }

  readonly results$ = this.select((s) => s.results);

  get hits() {
    return this.get((s) => s.hits);
  }

  readonly hits$ = this.select((s) => s.hits);

  get fetching() {
    return this.get((s) => s.fetching);
  }

  readonly fetching$ = this.select((s) => s.fetching);

  get queryParams() {
    return this.get((s) => s.queryParams);
  }

  readonly queryParams$ = this.select((s) => s.queryParams);

  get searchOptions() {
    return this.get((s) => s.searchOptions);
  }
  set searchOptions(searchOptions: { take?: number; skip?: number }) {
    this.patchState({ searchOptions });
  }

  private _searchResultSubject = new Subject<ListResponseArgsOfOrderItemListItemDTO>();

  readonly searchResult$ = this._searchResultSubject.asObservable();

  constructor(private _domainGoodsInService: DomainGoodsService) {
    super({
      fetching: false,
      hits: 0,
      results: [],
    });
  }

  loadSettings = this.effect(($) =>
    $.pipe(
      switchMap(() =>
        this._domainGoodsInService.goodsInQuerySettings().pipe(
          tapResponse(
            (res) => {
              this.setDefaultSettings(res.result);
              const filter = UiFilter.create(res.result);
              if (this.queryParams) {
                filter.fromQueryParams(this.queryParams);
              }
              this.setFilter(filter);
            },
            (err) => {
              console.error('GoodsInSearchStore.loadSettings()', err);
            }
          )
        )
      )
    )
  );

  setDefaultSettings(defaultSettings: QuerySettingsDTO) {
    this.patchState({ defaultSettings });
  }

  setFilter(filter: UiFilter | QuerySettingsDTO) {
    if (filter instanceof UiFilter) {
      this.patchState({ filter });
    } else {
      this.patchState({ filter: UiFilter.create(filter) });
    }
  }

  resetFilter(defaultQueryParams?: Record<string, string>) {
    const filter = UiFilter.create(this.defaultSettings);

    if (!!defaultQueryParams) {
      filter?.fromQueryParams(defaultQueryParams);
    }

    this.setFilter(filter);
  }

  clearResults() {
    this.patchState({
      fetching: false,
      hits: 0,
      message: undefined,
      results: [],
    });
  }

  setQueryParams(queryParams: Record<string, string>) {
    this.patchState({ queryParams });
    if (this.filter instanceof UiFilter) {
      this.filter?.fromQueryParams(queryParams);
      this.setFilter(this.filter);
    }
  }

  searchRequest(options?: { take?: number; skip?: number }) {
    return this.filter$.pipe(
      filter((f) => f instanceof UiFilter),
      take(1),
      mergeMap((filter) =>
        this._domainGoodsInService.searchWareneingang({
          ...filter.getQueryToken(),
          skip: options?.skip ?? this.results.length,
          take: options?.take ?? 50,
        })
      )
    );
  }

  search = this.effect(($) =>
    $.pipe(
      tap((_) => this.patchState({ fetching: true })),
      withLatestFrom(this.results$),
      switchMap(([_, _results]) =>
        this.searchRequest(this.searchOptions).pipe(
          tapResponse(
            (res) => {
              const results = [...(_results ?? []), ...(res.result ?? [])];

              this.patchState({
                hits: res.hits,
                results,
                fetching: false,
              });

              this._searchResultSubject.next(res);
            },
            (err: Error) => {
              if (err instanceof HttpErrorResponse && isResponseArgs(err.error)) {
                this._searchResultSubject.next(err.error);
              } else {
                this._searchResultSubject.next({
                  error: true,
                  message: err.message,
                });
              }
              this.patchState({ fetching: false });
              console.error('GoodsInSearchStore.search()', err);
            }
          )
        )
      )
    )
  );
}
