import { Component, ChangeDetectionStrategy } from '@angular/core';
import { Router } from '@angular/router';
import { ApplicationService } from '@core/application';
import { DomainCheckoutService } from '@domain/checkout';
import { AddToShoppingCartDTO, AvailabilityDTO, VATType } from '@swagger/checkout';
import { UiModalRef } from '@ui/modal';
import { shareReplay, debounceTime, first, map, switchMap, withLatestFrom } from 'rxjs/operators';
import { combineLatest, Observable } from 'rxjs';
import { PurchasingOptionsModalData } from './purchasing-options-modal.data';
import { PurchasingOptionsModalStore } from './purchasing-options-modal.store';
import { DomainCatalogService } from '@domain/catalog';
import { BreadcrumbService } from '@core/breadcrumb';

@Component({
  selector: 'page-purchasing-options-modal',
  templateUrl: 'purchasing-options-modal.component.html',
  styleUrls: ['purchasing-options-modal.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [PurchasingOptionsModalStore],
})
export class PurchasingOptionsModalComponent {
  readonly item$ = this.purchasingOptionsModalStore.selectItem;

  readonly availableOptions$ = this.purchasingOptionsModalStore.selectAvailableOptions.pipe(shareReplay());

  readonly option$ = this.purchasingOptionsModalStore.selectOption;

  readonly hasOption$ = this.purchasingOptionsModalStore.selectHasOption;

  readonly quantity$ = this.purchasingOptionsModalStore.selectQuantity;

  readonly canAdd$ = this.purchasingOptionsModalStore.selectCanAdd;

  readonly canAddError$ = this.purchasingOptionsModalStore.selectCanAddError;

  readonly canUpgrade$ = this.purchasingOptionsModalStore.selectCanUpgrade;

  readonly availability$ = this.purchasingOptionsModalStore.selectAvailability;

  readonly branch$ = this.purchasingOptionsModalStore.selectBranch;

  readonly canContinueShopping$ = this.purchasingOptionsModalStore.selectCanContinueShopping;

  readonly canContinueShoppingIsLoading$ = this.purchasingOptionsModalStore.selectCanContinueShoppingIsLoading;

  readonly quantityError$ = this.purchasingOptionsModalStore.selectQuantityError;

  readonly showCustomPrice$ = this.purchasingOptionsModalStore.selectAvailabilities.pipe(
    withLatestFrom(this.option$),
    map(([availabilities, option]) => !(availabilities[option]?.price?.value?.value || availabilities[option]?.retailPrice?.value?.value))
  );

  readonly customPriceInvalid$ = combineLatest([
    this.showCustomPrice$,
    this.purchasingOptionsModalStore.selectCustomPrice,
    this.purchasingOptionsModalStore.selectCustomVat,
  ]).pipe(map(([showCustomPrice, customPrice, customVat]) => showCustomPrice && (!customPrice || !customVat)));

  readonly showTakeAwayButton$ = combineLatest([
    this.option$,
    this.purchasingOptionsModalStore.selectFetchingAvailability,
    this.purchasingOptionsModalStore.selectCheckingCanAdd,
    this.canAdd$,
    this.canUpgrade$,
  ]).pipe(
    map(([option, fetchingAvailability, checkingCanAdd, canAdd, canUpgrade]) => {
      if (option !== 'take-away') {
        return false;
      }

      if (!fetchingAvailability && !checkingCanAdd && !canAdd) {
        return false;
      }

      return !canUpgrade;
    })
  );

  readonly showDefaultContinueButton$ = combineLatest([
    this.option$,
    this.purchasingOptionsModalStore.selectFetchingAvailability,
    this.purchasingOptionsModalStore.selectCheckingCanAdd,
    this.canAdd$,
    this.canUpgrade$,
  ]).pipe(
    map(([option, fetchingAvailability, checkingCanAdd, canAdd, canUpgrade]) => {
      if (option === 'take-away') {
        return false;
      }

      if (!fetchingAvailability && !checkingCanAdd && !canAdd) {
        return false;
      }

      return !canUpgrade;
    })
  );

  readonly showContinueWithoutAdding$ = combineLatest([this.showTakeAwayButton$, this.showDefaultContinueButton$, this.canUpgrade$]).pipe(
    map(([showTakeAway, showDefault, canUpgrade]) => !canUpgrade && !(showTakeAway || showDefault))
  );

  readonly showDeliveryInfo$ = this.option$.pipe(map((option) => ['delivery', 'b2b-delivery', 'dig-delivery'].indexOf(option) > -1));

  readonly fetching$ = combineLatest([
    this.purchasingOptionsModalStore.selectFetchingAvailability,
    this.purchasingOptionsModalStore.selectCheckingCanAdd,
  ]).pipe(map(([fetching, checking]) => fetching || checking));

  customerFeatures$ = this.application.activatedProcessId$.pipe(
    switchMap((processId) => this.checkoutService.getCustomerFeatures({ processId }))
  );

  readonly customerId$ = this.application.activatedProcessId$.pipe(
    switchMap((processId) => this.checkoutService.getBuyer({ processId })),
    map((buyer) => buyer.source)
  );

  price$ = combineLatest([
    this.purchasingOptionsModalStore.selectAvailabilities,
    this.option$,
    this.purchasingOptionsModalStore.selectCustomPrice,
  ]).pipe(
    map(([availabilities, option, customPrice]) => {
      if (option && !!availabilities[option]) {
        if (availabilities[option]?.price?.value?.value) {
          return availabilities[option]?.price?.value?.value;
        }
        return availabilities[option]?.retailPrice?.value?.value ?? customPrice;
      } else {
        const key = Object.keys(availabilities).find((key) => !!availabilities[key]?.price?.value?.value);
        return availabilities[key]?.price?.value?.value ?? customPrice;
      }
    })
  );

  vat$ = combineLatest([
    this.purchasingOptionsModalStore.selectAvailabilities,
    this.option$,
    this.purchasingOptionsModalStore.selectCustomVat,
  ]).pipe(
    map(([availabilities, option, customVat]) => {
      if (option && !!availabilities[option]) {
        if (availabilities[option]?.price?.vat?.vatType) {
          return availabilities[option]?.price?.vat?.vatType;
        }
        return availabilities[option]?.retailPrice?.vat?.vatType ?? customVat;
      } else {
        const key = Object.keys(availabilities).find((key) => !!availabilities[key]?.price?.vat?.vatType);
        return availabilities[key]?.price?.vat?.vatType ?? customVat;
      }
    })
  );

  readonly promoPoints$ = combineLatest([this.item$, this.quantity$, this.price$]).pipe(
    debounceTime(250),
    switchMap(([item, quantity, price]) =>
      this.domainCatalogService
        .getPromotionPoints({
          items: [
            {
              id: item.id,
              quantity: quantity,
              price: price,
            },
          ],
        })
        .pipe(map((res) => res.result[item.id]))
    )
  );

  quantityRange$ = combineLatest([this.option$, this.availability$]).pipe(
    map(([option, availability]) => (option === 'take-away' && availability?.inStock ? availability.inStock : 999))
  );

  activeSpinner: string;

  constructor(
    public modalRef: UiModalRef<any, PurchasingOptionsModalData>,
    public purchasingOptionsModalStore: PurchasingOptionsModalStore,
    private application: ApplicationService,
    private router: Router,
    private checkoutService: DomainCheckoutService,
    private domainCatalogService: DomainCatalogService,
    private breadcrumb: BreadcrumbService
  ) {
    this.purchasingOptionsModalStore.setShoppingCartItem(this.modalRef.data.shoppingCartItem);
    this.purchasingOptionsModalStore.setItem(this.modalRef.data.item);
    this.purchasingOptionsModalStore.setProcessId(this.modalRef.data.processId || this.application.activatedProcessId);
    this.purchasingOptionsModalStore.setAvailabilities(this.modalRef.data.availabilities || {});
    this.purchasingOptionsModalStore.setQuantity(this.modalRef?.data?.shoppingCartItem?.quantity || 1);
    this.purchasingOptionsModalStore.setOption(this.modalRef.data.option);
    this.purchasingOptionsModalStore.setAvailableOptions(this.modalRef.data.availableOptions);

    if (
      this.modalRef.data.availableOptions?.some((option) => option === 'pick-up' || option === 'take-away') ||
      ['take-away', 'pick-up'].includes(this.modalRef.data.option)
    ) {
      this.purchasingOptionsModalStore.loadBranches(this.modalRef?.data?.branchId);
    }
  }

  changeCustomVat(vat: VATType) {
    this.purchasingOptionsModalStore.setCustomVat(vat);
  }

  changeCustomPrice(price: number) {
    this.purchasingOptionsModalStore.setCustomPrice(price);
  }

  backToSetOptions() {
    this.purchasingOptionsModalStore.setOption(undefined);
  }

  getAvailability(option: string): Observable<AvailabilityDTO> {
    return this.purchasingOptionsModalStore.selectAvailabilities.pipe(map((ava) => ava[option]));
  }

  async changeQuantity(quantity: number = 1) {
    this.purchasingOptionsModalStore.setQuantity(quantity);

    if (quantity === 0) {
      this.modalRef.close();
    }
  }

  async continue(navigate: 'continue' | 'continue-shopping' | 'add-customer-data' = 'continue') {
    this.activeSpinner = navigate ? 'continue-shopping' : 'continue';
    try {
      const processId = await this.purchasingOptionsModalStore.selectProcessId.pipe(first()).toPromise();
      const customer = await this.checkoutService.getBuyer({ processId }).pipe(first()).toPromise();
      const item = await this.item$.pipe(first()).toPromise();
      const quantity = await this.quantity$.pipe(first()).toPromise();
      const availability = await this.availability$.pipe(first()).toPromise();
      const option = await this.option$.pipe(first()).toPromise();
      const branch = await this.branch$.pipe(first()).toPromise();
      const shoppingCartItem = await this.purchasingOptionsModalStore.selectShoppingCartItem.pipe(first()).toPromise();
      const canAdd = await this.canAdd$.pipe(first()).toPromise();
      const customPrice = await this.purchasingOptionsModalStore.selectCustomPrice.pipe(first()).toPromise();
      const customVat = await this.purchasingOptionsModalStore.selectCustomVat.pipe(first()).toPromise();

      if (canAdd || navigate === 'add-customer-data') {
        const newItem: AddToShoppingCartDTO = {
          quantity,
          availability,
          product: {
            catalogProductNumber: '',
            ...item.product,
          },
          promotion: { points: item.promoPoints },
        };

        newItem.product.catalogProductNumber = String(item.id);

        if (!!customPrice && !!customVat) {
          newItem.availability.price = {
            value: {
              value: customPrice,
              currency: 'EUR',
            },
            vat: {
              vatType: customVat,
            },
          };
        } else {
          const price = await this.price$.pipe(first()).toPromise();
          const vat = await this.vat$.pipe(first()).toPromise();
          newItem.availability.price = {
            value: {
              value: price,
              currency: 'EUR',
            },
            vat: {
              vatType: vat,
            },
          };
        }

        switch (option) {
          case 'take-away':
          case 'pick-up':
            newItem.destination = {
              data: { target: 1, targetBranch: { id: branch.id } },
            };
            break;
          case 'delivery':
          case 'dig-delivery':
          case 'b2b-delivery':
            newItem.destination = {
              data: { target: 2, logistician: availability.logistician },
            };
            break;
          case 'download':
            newItem.destination = {
              data: { target: 16, logistician: availability.logistician },
            };
            break;
        }

        if (shoppingCartItem) {
          await this.checkoutService
            .updateItemInShoppingCart({
              processId,
              shoppingCartItemId: shoppingCartItem?.id,
              update: {
                availability: newItem.availability,
                quantity: newItem.quantity,
                destination: newItem.destination,
              },
            })
            .toPromise();
        } else {
          await this.checkoutService
            .addItemToShoppingCart({
              processId,
              items: [newItem],
            })
            .toPromise();
        }
      }

      this.modalRef.close();

      if (shoppingCartItem) {
        return;
      }

      if (navigate === 'continue-shopping') {
        const crumbs = await this.breadcrumb
          .getBreadcrumbsByKeyAndTags$(processId, ['catalog', 'filter', 'results'])
          .pipe(first())
          .toPromise();
        if (!!crumbs && crumbs.length > 0) {
          const queryParams = crumbs[0].params;
          this.router.navigate(['/kunde', this.application.activatedProcessId, 'product', 'search', 'results'], { queryParams });
        } else {
          // Route back to search if no result page was loaded (f.e. When searching Article and landing directly on details page)
          this.router.navigate(['/kunde', this.application.activatedProcessId, 'product', 'search']);
        }
      } else if (navigate === 'continue') {
        // Set filter for navigation to customer search if customer is not set
        let filter: { [key: string]: string };
        if (!customer) {
          filter = await this.customerFeatures$
            .pipe(
              first(),
              switchMap((customerFeatures) => {
                return this.checkoutService.canSetCustomer({ processId, customerFeatures });
              }),
              map((res) => res.filter)
            )
            .toPromise();
          this.router.navigate(['/kunde', this.application.activatedProcessId, 'customer', 'search'], {
            queryParams: { filter_customertype: filter.customertype },
          });
        } else {
          this.router.navigate(['/kunde', this.application.activatedProcessId, 'cart', 'review']);
        }
      } else if (navigate === 'add-customer-data') {
        const upgradeCustomerId = await this.customerId$.pipe(first()).toPromise();
        this.router.navigate(['/kunde', this.application.activatedProcessId, 'customer', 'create', 'webshop'], {
          queryParams: { upgradeCustomerId },
        });
      }
    } catch (error) {
      console.log('PurchasingOptionsModalComponent.continue', error);
    }
    this.activeSpinner = undefined;
  }
}
