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

export interface GoodsInListState {
  message?: string;
  loading: boolean;
  hits: number;
  take: number;
  results: OrderItemListItemDTO[];
  filter: UiFilter;
}

@Injectable()
export class GoodsInListStore extends ComponentStore<GoodsInListState> {
  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 take() {
    return this.get((s) => s.take);
  }
  readonly take$ = this.select((s) => s.take);

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

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

  get loading() {
    return this.get((s) => s.loading);
  }
  readonly loading$ = this.select((s) => s.loading);

  private _searchResultSubject = new Subject<ListResponseArgsOfOrderItemListItemDTO>();

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

  constructor(private _domainGoodsInService: DomainGoodsService, private _router: Router) {
    super({
      loading: false,
      hits: 0,
      take: 25,
      results: [],
      filter: undefined,
    });
    this.setDefaultFilter();
  }

  search = this.effect((options$: Observable<{ clear?: boolean; cb?: Function }>) =>
    options$.pipe(
      tap((options) => this.patchState({ loading: true, results: options.clear ? [] : this.results })),
      withLatestFrom(this.results$, this.take$, this.filter$),
      switchMap(([options, _results, _take, _filter]) =>
        this.searchRequest({
          skip: _results?.length,
          take: _take,
          filter: _filter,
        }).pipe(
          tapResponse(
            (res) => {
              const result = res.result ?? [];

              const items = options.clear ? result : [..._results, ...result];
              this.patchState({
                hits: res.hits,
                results: items,
                take: 25,
                loading: false,
              });

              this._searchResultSubject.next(res);
              if (res.hits > 1) {
                const path = '/filiale/goods/in/list/';
                if (!this._router.isActive(path, false)) {
                  this._router.navigate([path], {
                    queryParams: { ...this.filter.getQueryParams(), take: res.result.length + _results?.length },
                  });
                }
              }

              options?.cb?.call(undefined);
            },
            (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({ loading: false });
              console.error('GoodsInListStore.search()', err);
            }
          )
        )
      )
    )
  );

  reload = this.effect(($) =>
    $.pipe(
      tap((_) => this.patchState({ loading: true })),
      withLatestFrom(this.results$, this.filter$),
      switchMap(([_, results, filter]) =>
        this.searchRequest({ take: results?.length ?? 0, skip: 0, filter }).pipe(
          tapResponse(
            (res) => {
              this.patchState({
                hits: res.hits,
                results: res.result,
                loading: 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({ loading: false });
              console.error('GoodsInListStore.reload()', err);
            }
          )
        )
      )
    )
  );

  async setDefaultFilter() {
    const filter = await this._domainGoodsInService
      .goodsInListQuerySettings()
      .pipe(map((settings) => UiFilter.create(settings.result)))
      .toPromise();

    this.setFilter(filter);
  }

  setFilter(filter: UiFilter) {
    this.patchState({ filter });
  }

  setTake(take: number) {
    this.patchState({ take });
  }

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

  searchRequest(options?: { take?: number; skip?: number; filter: UiFilter }) {
    const queryToken: QueryTokenDTO = {
      ...options?.filter?.getQueryToken(),
      skip: options?.skip ?? this.results.length,
      take: options?.take ?? 20,
    };
    return this._domainGoodsInService.goodsInList(queryToken);
  }
}
