import { HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { CacheService } from '@core/cache';
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 { combineLatest, Observable, Subject } from 'rxjs';
import { switchMap, mergeMap, withLatestFrom, filter, take, tap, debounceTime, first, map } from 'rxjs/operators';

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

@Injectable()
export class GoodsOutSearchStore extends ComponentStore<GoodsOutSearchState> {
  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);

  private _searchResultSubject = new Subject<ListResponseArgsOfOrderItemListItemDTO>();

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

  private _searchResultFromCacheSubject = new Subject<{ hits: number; results: OrderItemListItemDTO[] }>();

  readonly searchResultFromCache$ = this._searchResultFromCacheSubject.asObservable();

  private _searchResultClearedSubject = new Subject<void>();

  readonly searchResultCleared = this._searchResultClearedSubject.asObservable();

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

  loadSettings = this.effect(($) =>
    $.pipe(
      switchMap(() =>
        this._domainGoodsInService.goodsOutQuerySettings().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: [],
  //   });
  //   this._searchResultClearedSubject.next();
  // }

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

  // searchRequest(options: { skip?: number; take?: number }) {
  //   console.log('GoodsInSearchStore.searchRequest()', options);
  //   return this.filter$.pipe(
  //     filter((f) => f instanceof UiFilter),
  //     take(1),
  //     mergeMap((filter) => {
  //       console.log('GoodsInSearchStore.searchRequest()', filter);
  //       return this._domainGoodsInService.searchWarenausgabe({
  //         ...filter.getQueryToken(),
  //         skip: options.skip,
  //         take: options.take,
  //       });
  //     })
  //   );
  // }

  search = this.effect((options$: Observable<{ clear?: boolean; siletReload?: boolean }>) =>
    options$.pipe(
      switchMap((options) => {
        return this.results$.pipe(
          map((results) => [options, results]),
          first()
        );
      }),
      switchMap(([options, results]) => {
        return this.filter$.pipe(
          filter((f) => f instanceof UiFilter),
          map((filter) => [options, results, filter]),
          first()
        );
      }),
      tap(([options, results, filter]: [{ clear?: boolean; siletReload?: boolean }, OrderItemListItemDTO[], UiFilter]) => {
        if (!options?.siletReload) {
          this.patchState({ fetching: true });
        }
        if (options?.clear) {
          this._searchResultClearedSubject.next();
        }
      }),
      switchMap(([options, results, filter]) => {
        const queryToken = filter?.getQueryToken() ?? {};

        let cachedResultCount: number;

        if (options?.siletReload && this._cache.get(filter?.getQueryToken())) {
          const cachedResults = this._cache.get(queryToken);
          if (cachedResults?.results?.length > 0) {
            this.patchState(cachedResults);
            cachedResultCount = cachedResults.results.length;

            this._searchResultFromCacheSubject.next({ hits: cachedResults.hits, results: cachedResults.results });
          }
        }

        if (options.clear) {
          queryToken.skip = 0;
          queryToken.take = 50;
        } else if (options.siletReload) {
          queryToken.skip = 0;
          queryToken.take = cachedResultCount || results.length || 50;
        } else {
          queryToken.skip = results.length;
          queryToken.take = 50;
        }

        return this._domainGoodsInService.searchWarenausgabe(queryToken).pipe(
          tapResponse(
            (res) => {
              let _results: OrderItemListItemDTO[] = [];
              if (options.siletReload) {
                _results = res.result;
              } else if (options.clear) {
                _results = res.result;
              } else {
                _results = [...results, ...(res.result ?? [])];
              }

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

              this._cache.set(filter?.getQueryToken(), {
                hits: res.hits,
                results: _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);
            }
          )
        );
      })
    )
  );

  // reload = this.effect(($) =>
  //   $.pipe(
  //     tap((_) => this.patchState({ fetching: true })),
  //     withLatestFrom(this.results$),
  //     switchMap(([_, results]) =>
  //       this.searchRequest({ reload: true }).pipe(
  //         tapResponse(
  //           (res) => {
  //             this.patchState({
  //               hits: res.hits,
  //               results: res.result,
  //               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.reload()', err);
  //           }
  //         )
  //       )
  //     )
  //   )
  // );
}
