import { Injectable } from '@angular/core';
import { DomainPrinterService } from '@domain/printer';
import { DomainProductListService } from '@domain/product-list';
import { PrintModalComponent, PrintModalData } from '@modal/printer';
import { ComponentStore, OnStoreInit } from '@ngrx/component-store';
import {
  BatchResponseArgsOfProductListItemDTOAndString,
  ListResponseArgsOfProductListItemDTO,
  ProductListItemDTO,
  QuerySettingsDTO,
} from '@generated/swagger/wws-api';
import { UiFilter } from '@ui/filter';
import { UiModalService } from '@ui/modal';
import { isNil } from 'lodash';
import { Subject } from 'rxjs';
import { switchMap, takeUntil, tap, withLatestFrom } from 'rxjs/operators';
import { INITIAL_PRICE_UPDATE_COMPONENT_STATE, PriceUpdateComponentState } from './price-update.component.state';
import { injectCancelSearchSubject } from '@shared/services/cancel-subject';
import { tapResponse } from '@ngrx/operators';

@Injectable()
export class PriceUpdateComponentStore extends ComponentStore<PriceUpdateComponentState> implements OnStoreInit {
  private _cancelSubject = injectCancelSearchSubject();

  showFilter$ = this.select((state) => state.showFilter);

  fetching$ = this.select((state) => state.fetching);

  loading$ = this.select((state) => state.loading);

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

  get filter(): UiFilter {
    return this.get((f) => f.filter);
  }

  pendingFilter$ = this.select((state) => state.pendingFilter);

  defaultFilter$ = this.select((state) => state.defaultFilter);

  items$ = this.select((state) => state.items);

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

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

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

  private _onFetchingItemsResponse$ = new Subject<ListResponseArgsOfProductListItemDTO>();

  onFetchingItemsResponse$ = this._onFetchingItemsResponse$.asObservable();

  constructor(
    private _productListService: DomainProductListService,
    private _domainPrinterService: DomainPrinterService,
    private _uiModal: UiModalService,
  ) {
    super(INITIAL_PRICE_UPDATE_COMPONENT_STATE);
  }

  ngrxOnStoreInit() {
    this.fetchSettings();
  }

  setShowFilter = this.updater((state, showFilter: boolean) => ({ ...state, showFilter }));

  setFetching = this.updater((state, fetching: boolean) => ({ ...state, fetching }));

  setLoading = this.updater((state, loading: boolean) => ({ ...state, loading }));

  setFilter = this.updater((state, filter: UiFilter) => ({ ...state, filter }));

  setPendingFilter = this.updater((state, pendingFilter: UiFilter) => ({ ...state, pendingFilter }));

  setDefaultFilter = this.updater((state, defaultFilter: UiFilter) => ({ ...state, defaultFilter }));

  setItems = this.updater((state, items: ProductListItemDTO[]) => ({ ...state, items }));

  setSelected({ selected, itemUid }: { selected: boolean; itemUid: string }) {
    if (selected) {
      this.patchState({
        selectedItemUids: [...this.selectedItemUids, itemUid],
      });
    } else if (!selected) {
      this.patchState({
        selectedItemUids: this.selectedItemUids.filter((id) => id !== itemUid),
      });
    }
  }

  fetchSettings = this.effect(($) =>
    $.pipe(
      switchMap((_) =>
        this._productListService.getQuerySettings().pipe(tapResponse(this.onFetchSettingsResponse, this.onFetchError)),
      ),
    ),
  );

  cancelFetchItems = () => this._cancelSubject.next();

  fetchItems = this.effect(($) =>
    $.pipe(
      tap(() => this.beforeFetchingItems()),
      withLatestFrom(this.filter$),
      switchMap(([_, filter]) =>
        this._productListService.queryProductListResponse(filter.getQueryToken()).pipe(
          takeUntil(this._cancelSubject),
          tapResponse({
            next: this.onFetchingItemsResponse,
            error: this.onFetchError,
            finalize: () => this.setFetching(false),
          }),
        ),
      ),
    ),
  );

  complete = this.effect(($) =>
    $.pipe(
      tap(() => this.beforeComplete()),
      withLatestFrom(this.selectedItemUids$),
      switchMap(([_, selectedItemUids]) =>
        this._productListService
          .completeProductListItems(selectedItemUids)
          .pipe(tapResponse(this.onCompleteResponse, this.onCompleteError)),
      ),
    ),
  );

  print() {
    const selectableItems = this.items.filter((item) => this.isSelectable(item));
    if (selectableItems?.length > 0) {
      this._uiModal.open({
        content: PrintModalComponent,
        data: {
          printerType: 'Office',
          print: (printer) =>
            this._domainPrinterService
              .printProductListItems({ data: selectableItems, printer, title: 'Preisänderungen' })
              .toPromise(),
        } as PrintModalData,
        config: {
          panelClass: [],
          showScrollbarY: false,
        },
      });
    }
  }

  beforeComplete() {
    this.setLoading(true);
  }

  beforeFetchingItems() {
    this.setFetching(true);
    this.setItems([]);
    this.patchState({ selectedItemUids: [] });
  }

  onCompleteResponse = (response: BatchResponseArgsOfProductListItemDTOAndString) => {
    let items = this.items;

    if (response.error) {
      this._uiModal.error(
        'Setzen der Produkte auf Erledigt enthält fehlerhafte Einträge.',
        new Error(response.message),
      );
    } else if (response.successful) {
      items = items.filter((item) => !response.successful.some((s) => s.key === item.uId));
    } else {
      this._uiModal.error(
        'Setzen der Produkte auf Erledigt enthält keine erfolgreichen Einträge.',
        new Error('Keine erfolgreichen Einträge'),
      );
    }

    this.patchState({ selectedItemUids: [] });
    this.setItems(items);
    this.setLoading(false);
  };

  onCompleteError = (error: any) => {
    console.error(error);
    this._uiModal.error('Setzen der Produkte auf Erledigt fehlgeschlagen.', error);
    this.setLoading(false);
  };

  onFetchSettingsResponse = (querySettings: QuerySettingsDTO) => {
    const filter = UiFilter.create(querySettings);
    this.setDefaultFilter(UiFilter.create(filter));
    this.setPendingFilter(UiFilter.create(filter));
    this.setFilter(UiFilter.create(filter));
  };

  onFetchingItemsResponse = (response: ListResponseArgsOfProductListItemDTO) => {
    this.setFetching(false);
    this.setItems(response.result);
    this._onFetchingItemsResponse$.next(response);
  };

  onFetchError = (error: any) => {
    console.error(error);
    this._uiModal.error('Laden der Produktliste fehlgeschlagen.', error);
    this.setFetching(false);
    this.setItems([]);
  };

  isSelectable(item: ProductListItemDTO) {
    // True wenn das Item das aktuelle oder ein älteres Datum beim gültig ab Wert hat
    return new Date(item?.task?.dueDate) <= new Date(Date.now()) && isNil(item.task.completed);
  }

  restorePendingFilter = this.updater((state) => ({ ...state, pendingFilter: UiFilter.create(state.filter) }));

  resetPendingFilter = this.updater((state) => ({ ...state, pendingFilter: UiFilter.create(state.defaultFilter) }));

  applyPendingFilterToFilter = this.updater((state) => ({ ...state, filter: UiFilter.create(state.pendingFilter) }));
}
