import { Injectable } from '@angular/core';
import { DomainAvailabilityService } from '@domain/availability';
import { ComponentStore } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';

import { ArrivalStatus, PackageItemDTO, StockInfoDTO } from '@generated/swagger/wws-api';
import { Observable } from 'rxjs';
import { filter, switchMap } from 'rxjs/operators';

export interface PackageDetailsListState {
  items: PackageItemDTO[];
  stockInfos: Record<string, StockInfoDTO>;
  arrivalStatus: ArrivalStatus;
}

@Injectable()
export class PackageDetailsListStore extends ComponentStore<PackageDetailsListState> {
  static ITEMS_SELECTOR = (state: PackageDetailsListState) => {
    const { arrivalStatus, items } = state;

    // 0 = Offen 4 = Fehlt
    if (arrivalStatus === 4 || arrivalStatus === 0) {
      const result = [...items];

      result.sort((a, b) => {
        const aInStock = PackageDetailsListStore.IN_STOCK_BY_EAN_SELECTOR(a.product.ean)(state);
        const bInStock = PackageDetailsListStore.IN_STOCK_BY_EAN_SELECTOR(b.product.ean)(state);

        return aInStock - bInStock;
      });

      return result;
    }

    return items;
  };

  static STOCKINFOS_SELECTOR = (state: PackageDetailsListState) => state.stockInfos;

  static ARRIVAL_STATUS_SELECTOR = (state: PackageDetailsListState) => state.arrivalStatus;

  static ITEM_BY_EAN = (ean: string) => (state: PackageDetailsListState) =>
    state.items.find((i) => i.product.ean === ean);

  static STOCKINFO_BY_EAN = (ean: string) => (state: PackageDetailsListState) => state.stockInfos[ean];

  static IN_STOCK_BY_EAN_SELECTOR = (ean: string) => (state: PackageDetailsListState) => {
    const stockInfoInStock = PackageDetailsListStore.STOCKINFO_BY_EAN(ean)(state)?.inStock;
    const item = PackageDetailsListStore.ITEM_BY_EAN(ean)(state);

    if (stockInfoInStock !== undefined) {
      const inStock = stockInfoInStock;
      // Wenn der Status 0 ist, also "Offen", dann wird die Anzahl der fehlenden Artikel abgezogen
      if (state.arrivalStatus === 0) {
        return inStock - (item?.quantity ?? 0);
      } else {
        return inStock;
      }
    }

    const itemInStock = item?.stockInfos?.reduce((acc, stockInfo) => acc + stockInfo.inStock, 0) ?? 0;

    // Wenn der Status 0 ist, also "Offen", dann wird die Anzahl der fehlenden Artikel abgezogen
    if (state.arrivalStatus === 0) {
      return itemInStock - (item?.quantity ?? 0);
    } else {
      return itemInStock;
    }
  };

  static SSC_TEXT_BY_EAN = (ean: string) => (state: PackageDetailsListState) => {
    return state.items.find((i) => i.product.ean === ean)?.ssc;
  };

  static COMPARTMENTY_BY_EAN = (ean: string) => (state: PackageDetailsListState) => {
    const stockInfoCompartment = PackageDetailsListStore.STOCKINFO_BY_EAN(ean)(state)?.compartment;

    if (stockInfoCompartment) {
      return stockInfoCompartment;
    }

    const item = PackageDetailsListStore.ITEM_BY_EAN(ean)(state);

    return item?.stockInfos?.reduce((acc, stockInfo) => [...acc, stockInfo.compartment], [])?.join(';') ?? '';
  };

  get items() {
    return this.get(PackageDetailsListStore.ITEMS_SELECTOR);
  }

  items$ = this.select(PackageDetailsListStore.ITEMS_SELECTOR);

  get stockInfos() {
    return this.get(PackageDetailsListStore.STOCKINFOS_SELECTOR);
  }

  stockInfos$ = this.select(PackageDetailsListStore.STOCKINFOS_SELECTOR);

  get arrivalStatus() {
    return this.get(PackageDetailsListStore.ARRIVAL_STATUS_SELECTOR);
  }

  arrivalStatus$ = this.select(PackageDetailsListStore.ARRIVAL_STATUS_SELECTOR);

  constructor(private _availableStockService: DomainAvailabilityService) {
    super({
      items: [],
      stockInfos: {},
      arrivalStatus: 0,
    });
  }

  loadStockInfos = this.effect((items$: Observable<PackageItemDTO[]>) =>
    items$.pipe(
      filter((items) => items.length > 0),
      switchMap((items) =>
        this._availableStockService
          .getInStockByEan({
            eans: items.map((i) => i.product.ean),
          })
          .pipe(tapResponse(this.onLoadStockInfosResponse, this.onLoadStockInfosError)),
      ),
    ),
  );

  onLoadStockInfosResponse = (stockInfos: Record<string, StockInfoDTO>) => {
    this.patchState({ stockInfos });
  };

  onLoadStockInfosError = (error: any) => {
    console.error(error);
  };

  setItems(items: PackageItemDTO[], loadStockInfos: boolean) {
    this.patchState({ items });
    if (items.length === 0) return;

    if (loadStockInfos) {
      this.loadStockInfos(items);
    }
  }

  setArrivalStatus(arrivalStatus: ArrivalStatus) {
    this.patchState({ arrivalStatus });
  }

  getInStockByEan(ean: string) {
    return this.get(PackageDetailsListStore.IN_STOCK_BY_EAN_SELECTOR(ean));
  }

  getInStockByEan$(ean: string) {
    return this.select(PackageDetailsListStore.IN_STOCK_BY_EAN_SELECTOR(ean));
  }

  getSscTextByEan$(ean: string) {
    return this.select(PackageDetailsListStore.SSC_TEXT_BY_EAN(ean));
  }

  getCompartmentByEan(ean: string) {
    return this.get(PackageDetailsListStore.COMPARTMENTY_BY_EAN(ean));
  }

  getCompartmentByEan$(ean: string) {
    return this.select(PackageDetailsListStore.COMPARTMENTY_BY_EAN(ean));
  }
}
