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

export interface GoodsInReservationState {
  message?: string;
  loading: boolean;
  hits: number;
  results: OrderItemListItemDTO[];
  selectedOrderItemSubsetIds: number[];
  searchOptions?: { take?: number; skip?: number };
}

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

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

  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();

  readonly takeAwayAvailabilities$ = this.results$.pipe(
    switchMap((items) => {
      const eans = items?.map((item) => item?.product?.ean);
      return this._availabilityService.getTakeAwayAvailabilitiesByEans({ eans });
    })
  );

  constructor(private _domainGoodsInService: DomainGoodsService, private _availabilityService: DomainAvailabilityService) {
    super({
      loading: false,
      hits: 0,
      results: [],
      selectedOrderItemSubsetIds: [],
    });
  }

  search = this.effect(($) =>
    $.pipe(
      tap((_) => this.patchState({ loading: true })),
      withLatestFrom(this.results$),
      switchMap(([_, _results]) =>
        this.searchRequest(this.searchOptions).pipe(
          tapResponse(
            (res) => {
              const results = [...(_results ?? []), ...(res.result ?? [])];
              this.patchState({
                hits: res.hits,
                results,
                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('GoodsInReservationStore.search()', err);
            }
          )
        )
      )
    )
  );

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

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

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