import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
import { DomainAvailabilityService } from '@domain/availability';
import { DomainCheckoutService } from '@domain/checkout';
import { ShoppingCartItemDTO, UpdateShoppingCartItemDTO } from '@swagger/checkout';
import { UiErrorModalComponent, UiModalRef, UiModalService } from '@ui/modal';
import { BehaviorSubject, combineLatest, Subject } from 'rxjs';
import { debounceTime, filter, first, map, shareReplay, takeUntil, withLatestFrom } from 'rxjs/operators';
import { PurchasingOptionsListModalData } from './purchasing-options-list-modal.data';
import { PurchasingOptionsListModalStore } from './purchasing-options-list-modal.store';

@Component({
  selector: 'page-purchasing-options-list-modal',
  templateUrl: 'purchasing-options-list-modal.component.html',
  styleUrls: ['purchasing-options-list-modal.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [PurchasingOptionsListModalStore],
})
export class PurchasingOptionsListModalComponent implements OnInit {
  private _onDestroy$ = new Subject();

  addItemsLoader$ = new BehaviorSubject<boolean>(false);

  shoppingCartItems$ = combineLatest([
    this._store.fetchingAvailabilities$,
    this._store.selectedFilterOption$,
    this._store.shoppingCartItems$,
  ]).pipe(
    withLatestFrom(
      this._store.takeAwayAvailabilities$,
      this._store.pickUpAvailabilities$,
      this._store.deliveryAvailabilities$,
      this._store.deliveryDigAvailabilities$,
      this._store.deliveryB2bAvailabilities$
    ),
    map(
      ([
        [_, selectedFilterOption, shoppingCartItems],
        takeAwayAvailability,
        pickUpAvailability,
        deliveryAvailability,
        deliveryDigAvailability,
        deliveryB2bAvailability,
      ]) => {
        if (!!takeAwayAvailability && !!pickUpAvailability && !!deliveryAvailability) {
          switch (selectedFilterOption) {
            case 'take-away':
              return shoppingCartItems.filter((item) => !!takeAwayAvailability[item.product?.catalogProductNumber]);
            case 'pick-up':
              return shoppingCartItems.filter((item) => !!pickUpAvailability[item.product?.catalogProductNumber]);
            case 'delivery':
              return shoppingCartItems.filter(
                (item) =>
                  !!deliveryAvailability[item.product?.catalogProductNumber] ||
                  !!deliveryDigAvailability[item.product?.catalogProductNumber] ||
                  !!deliveryB2bAvailability[item.product?.catalogProductNumber]
              );
          }
        }
        return shoppingCartItems;
      }
    ),
    map((shoppingCartItems) => shoppingCartItems?.sort((a, b) => a.product?.name.localeCompare(b.product?.name))),
    shareReplay()
  );

  selectedShoppingCartItems$ = this._store.selectedShoppingCartItems$;

  allShoppingCartItemsSelected$ = combineLatest([this.shoppingCartItems$, this.selectedShoppingCartItems$]).pipe(
    map(
      ([shoppingCartItems, selectedShoppingCartItems]) =>
        shoppingCartItems.every((item) => selectedShoppingCartItems.find((i) => item.id === i.id)) && shoppingCartItems?.length > 0
    )
  );

  canAddItems$ = this._store.canAdd$.pipe(
    map((canAdd) => {
      for (const key in canAdd) {
        if (Object.prototype.hasOwnProperty.call(canAdd, key)) {
          if (!!canAdd[key]?.message) {
            return false;
          }
        }
      }
      return true;
    }),
    shareReplay()
  );

  selectAllCtaDisabled$ = combineLatest([this._store.selectedFilterOption$, this.canAddItems$]).pipe(
    withLatestFrom(this.shoppingCartItems$),
    map(([[selectedFilterOption, canAddItems], items]) => !selectedFilterOption || items?.length === 0 || !canAddItems)
  );

  applyCtaDisabled$ = combineLatest([this.addItemsLoader$, this._store.selectedFilterOption$, this._store.selectedShoppingCartItems$]).pipe(
    withLatestFrom(this.shoppingCartItems$),
    map(
      ([[addItemsLoader, selectedFilterOption, selectedShoppingCartItems], shoppingCartItems]) =>
        addItemsLoader || !selectedFilterOption || shoppingCartItems?.length === 0 || selectedShoppingCartItems?.length === 0
    )
  );

  constructor(
    private _modalRef: UiModalRef<any, PurchasingOptionsListModalData>,
    private _modal: UiModalService,
    private _store: PurchasingOptionsListModalStore,
    private _availability: DomainAvailabilityService,
    private _checkout: DomainCheckoutService
  ) {
    this._store.shoppingCartItems = _modalRef.data.shoppingCartItems;
    this._store.customerFeatures = _modalRef.data.customerFeatures;
    this._store.processId = _modalRef.data.processId;
  }

  ngOnInit() {
    this._store.loadBranches();

    // Beim Wechsel der ausgewählten Filteroption oder der Branches die Auswahl leeren
    combineLatest([this._store.selectedFilterOption$, this._store.selectedTakeAwayBranch$, this._store.selectedPickUpBranch$])
      .pipe(takeUntil(this._onDestroy$))
      .subscribe(() => this._store.clearSelectedShoppingCartItems());

    this._store.selectedFilterOption$
      .pipe(takeUntil(this._onDestroy$), withLatestFrom(this.shoppingCartItems$))
      .subscribe(([option, items]) => this.checkCanAdd(option, items));

    this._store.fetchingAvailabilities$
      .pipe(
        takeUntil(this._onDestroy$),
        debounceTime(250),
        filter((fetching) => !fetching),
        withLatestFrom(this.shoppingCartItems$, this._store.selectedFilterOption$)
      )
      .subscribe(([_, items, option]) => this.checkCanAdd(option, items));

    this.canAddItems$
      .pipe(takeUntil(this._onDestroy$), withLatestFrom(this.shoppingCartItems$, this._store.selectedFilterOption$))
      .subscribe(([showSelectAll, items, option]) => {
        if (items?.length > 0 && this._store.lastSelectedFilterOption$.value !== option) {
          this.selectAll(items, showSelectAll && !!option);
        }

        // Nach dem Übernehmen von Items wird eine neue CanAdd Abfrage ausgeführt, in diesem Fall soll aber nicht alles ausgewählt werden
        this._store.lastSelectedFilterOption$.next(option);
      });
  }

  checkCanAdd(selectedFilterOption: string, items: ShoppingCartItemDTO[]) {
    if (!!selectedFilterOption && items?.length > 0) {
      this._store.checkCanAddItems(items);
    } else {
      this._store.patchState({ canAdd: {} });
    }
  }

  async selectAll(items: ShoppingCartItemDTO[], value: boolean) {
    this._store.selectShoppingCartItem(items, value);
  }

  async apply() {
    this.addItemsLoader$.next(true);

    try {
      const shoppingCartItems = await this._store.shoppingCartItems$.pipe(first()).toPromise();
      const items = await this._store.selectedShoppingCartItems$.pipe(first()).toPromise();
      const takeAwayAvailabilities = await this._store.takeAwayAvailabilities$.pipe(first()).toPromise();
      const pickupAvailabilities = await this._store.pickUpAvailabilities$.pipe(first()).toPromise();
      const deliveryAvailabilities = await this._store.deliveryAvailabilities$.pipe(first()).toPromise();
      const deliveryB2bAvailabilities = await this._store.deliveryB2bAvailabilities$.pipe(first()).toPromise();
      const deliveryDigAvailabilities = await this._store.deliveryDigAvailabilities$.pipe(first()).toPromise();
      const selectedTakeAwayBranch = await this._store.selectedTakeAwayBranch$.pipe(first()).toPromise();
      const selectedPickUpBranch = await this._store.selectedPickUpBranch$.pipe(first()).toPromise();
      let option = this._store.selectedFilterOption;

      for (const item of items) {
        let availability;
        switch (this._store.selectedFilterOption) {
          case 'take-away':
            availability = takeAwayAvailabilities[item.product.catalogProductNumber];
            break;
          case 'pick-up':
            availability = pickupAvailabilities[item.product.catalogProductNumber];
            break;
          case 'delivery':
            if (
              deliveryDigAvailabilities[item.product.catalogProductNumber] &&
              deliveryB2bAvailabilities[item.product.catalogProductNumber] &&
              deliveryAvailabilities[item.product.catalogProductNumber]
            ) {
              availability = deliveryAvailabilities[item.product.catalogProductNumber];
            } else if (deliveryDigAvailabilities[item.product.catalogProductNumber]) {
              availability = deliveryDigAvailabilities[item.product.catalogProductNumber];
            } else if (deliveryB2bAvailabilities[item.product.catalogProductNumber]) {
              availability = deliveryB2bAvailabilities[item.product.catalogProductNumber];
              option = 'b2b-delivery';
            }
            break;
        }

        const price = this._availability.getPriceForAvailability(option, item.availability, availability);

        // Negative Preise und nicht vorhandene Availability ignorieren
        if (price?.value?.value < 0 || !availability) {
          continue;
        }

        const updateItem: UpdateShoppingCartItemDTO = {
          quantity: item.quantity,
          availability: {
            ...availability,
            price,
          },
          promotion: { points: item.promotion.points },
        };

        switch (this._store.selectedFilterOption) {
          case 'take-away':
            updateItem.destination = {
              data: { target: 1, targetBranch: { id: selectedTakeAwayBranch.id } },
            };
            break;
          case 'pick-up':
            updateItem.destination = {
              data: { target: 1, targetBranch: { id: selectedPickUpBranch.id } },
            };
            break;
          case 'delivery':
          case 'dig-delivery':
          case 'b2b-delivery':
            updateItem.destination = {
              data: { target: 2, logistician: availability?.logistician },
            };
            break;
        }

        await this._checkout
          .updateItemInShoppingCart({
            processId: this._modalRef.data.processId,
            shoppingCartItemId: item.id,
            update: {
              ...updateItem,
            },
          })
          .toPromise();
      }

      const remainingItems = shoppingCartItems.filter((i) => !items.find((j) => i.id === j.id));
      this._store.shoppingCartItems = [...remainingItems];

      this._store.clearSelectedShoppingCartItems();

      if (remainingItems?.length === 0) {
        this._modalRef.close();
      }
    } catch (error) {
      this._modal.open({ content: UiErrorModalComponent, data: error, title: 'Fehler beim Hinzufügen zum Warenkorb' });
    } finally {
      this.addItemsLoader$.next(false);
    }

    const shoppingCartItems = await this.shoppingCartItems$.pipe(first()).toPromise();
    if (shoppingCartItems?.length > 0) {
      this._store.checkCanAddItems(shoppingCartItems);
    }
  }
}
