import { Component, ChangeDetectionStrategy, ChangeDetectorRef, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { ApplicationService } from '@core/application';
import { DomainAvailabilityService } from '@domain/availability';
import { DomainCheckoutService } from '@domain/checkout';
import { AvailabilityDTO, DestinationDTO, NotificationChannel, ShoppingCartItemDTO, ShoppingCartDTO } from '@swagger/checkout';
import { UiErrorModalComponent, UiMessageModalComponent, UiModalService } from '@ui/modal';
import { PrintModalData, PrintModalComponent } from '@modal/printer';
import { PurchasingOptionsModalComponent, PurchasingOptionsModalData } from '../modals/purchasing-options-modal';
import { PurchasingOptions } from '../modals/purchasing-options-modal/purchasing-options-modal.store';
import { AuthService } from '@core/auth';
import { first, map, shareReplay, switchMap, takeUntil, tap, withLatestFrom } from 'rxjs/operators';
import { Subject, NEVER, combineLatest, BehaviorSubject } from 'rxjs';
import { DomainCatalogService } from '@domain/catalog';
import { BreadcrumbService } from '@core/breadcrumb';
import { DomainPrinterService } from '@domain/printer';
import { CheckoutDummyComponent } from '../checkout-dummy/checkout-dummy.component';
import { ResponseArgsOfItemDTO } from '@swagger/cat';
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
import { emailNotificationValidator, mobileNotificationValidator } from '@shared/notification-channel-control';
import { PurchasingOptionsListModalComponent } from '../modals/purchasing-options-list-modal';
import { PurchasingOptionsListModalData } from '../modals/purchasing-options-list-modal/purchasing-options-list-modal.data';
import { ComponentStore, tapResponse } from '@ngrx/component-store';

export interface CheckoutReviewComponentState {
  shoppingCart: ShoppingCartDTO;
  shoppingCartItems: ShoppingCartItemDTO[];
  fetching: boolean;
}

@Component({
  selector: 'page-checkout-review',
  templateUrl: 'checkout-review.component.html',
  styleUrls: ['checkout-review.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CheckoutReviewComponent extends ComponentStore<CheckoutReviewComponentState> implements OnInit {
  private _orderCompleted = new Subject<void>();

  get shoppingCart() {
    return this.get((s) => s.shoppingCart);
  }
  set shoppingCart(shoppingCart: ShoppingCartDTO) {
    this.patchState({ shoppingCart });
  }
  readonly shoppingCart$ = this.select((s) => s.shoppingCart);

  get shoppingCartItems() {
    return this.get((s) => s.shoppingCartItems);
  }
  set shoppingCartItems(shoppingCartItems: ShoppingCartItemDTO[]) {
    this.patchState({ shoppingCartItems });
  }
  readonly shoppingCartItems$ = this.select((s) => s.shoppingCartItems);

  get fetching() {
    return this.get((s) => s.fetching);
  }
  set fetching(fetching: boolean) {
    this.patchState({ fetching });
  }
  readonly fetching$ = this.select((s) => s.fetching);

  payer$ = this.applicationService.activatedProcessId$.pipe(
    takeUntil(this._orderCompleted),
    switchMap((processId) => this.domainCheckoutService.getPayer({ processId })),
    shareReplay()
  );

  shippingAddress$ = this.applicationService.activatedProcessId$.pipe(
    takeUntil(this._orderCompleted),
    switchMap((processId) => this.domainCheckoutService.getShippingAddress({ processId }))
  );

  shoppingCartItemsWithoutOrderType$ = this.shoppingCartItems$.pipe(
    takeUntil(this._orderCompleted),
    map((items) => items?.filter((item) => item?.features?.orderType === undefined))
  );

  groupedItems$ = this.shoppingCartItems$.pipe(
    takeUntil(this._orderCompleted),
    map((items) =>
      items.reduce((grouped, item) => {
        let index = grouped.findIndex((g) =>
          item?.availability?.supplyChannel === 'MANUALLY'
            ? g?.orderType === 'Dummy'
            : item?.features?.orderType === 'DIG-Versand'
            ? g?.orderType === 'Versand'
            : g?.orderType === item?.features?.orderType
        );

        let group = index !== -1 ? grouped[index] : undefined;
        if (!group) {
          group = {
            orderType:
              item?.availability?.supplyChannel === 'MANUALLY'
                ? 'Dummy'
                : item?.features?.orderType === 'DIG-Versand'
                ? 'Versand'
                : item?.features?.orderType,
            destination: item?.destination?.data,
            items: [],
          };
        }

        group.items = [...group.items, item]?.sort(
          (a, b) =>
            a.destination?.data?.targetBranch?.id - b.destination?.data?.targetBranch?.id || a.product?.name.localeCompare(b.product?.name)
        );

        if (index !== -1) {
          grouped[index] = group;
        } else {
          grouped.push(group);
        }

        return [...grouped].sort((a, b) => (a?.orderType === undefined ? -1 : b?.orderType === undefined ? 1 : 0));
      }, [] as { orderType: string; destination: DestinationDTO; items: ShoppingCartItemDTO[] }[])
    )
  );

  specialComment$ = this.applicationService.activatedProcessId$.pipe(
    switchMap((processId) => this.domainCheckoutService.getSpecialComment({ processId }))
  );

  totalItemCount$ = this.shoppingCartItems$.pipe(
    takeUntil(this._orderCompleted),
    map((items) => items.reduce((total, item) => total + item.quantity, 0))
  );

  totalReadingPoints$ = this.shoppingCartItems$.pipe(
    switchMap((displayOrders) => {
      if (displayOrders.length === 0) {
        return NEVER;
      }
      const items = displayOrders
        .map((i) => {
          if (i?.product?.catalogProductNumber) {
            return {
              id: Number(i.product?.catalogProductNumber),
              quantity: i.quantity,
              price: i.unitPrice?.value?.value,
            };
          }
        })
        .filter((item) => item !== undefined);
      if (items.length !== 0) {
        return this.domainCatalogService
          .getPromotionPoints({
            items,
          })
          .pipe(map((response) => Object.values(response.result).reduce((sum, points) => sum + points, 0)));
      } else {
        return NEVER;
      }
    })
  );

  customerFeatures$ = this.applicationService.activatedProcessId$.pipe(
    takeUntil(this._orderCompleted),
    switchMap((processId) => this.domainCheckoutService.getCustomerFeatures({ processId }))
  );

  control: FormGroup;

  showBillingAddress$ = this.shoppingCartItems$.pipe(
    takeUntil(this._orderCompleted),
    withLatestFrom(this.customerFeatures$),
    map(
      ([items, customerFeatures]) =>
        items.some(
          (item) =>
            item.features?.orderType === 'Versand' ||
            item.features?.orderType === 'B2B-Versand' ||
            item.features?.orderType === 'DIG-Versand'
        ) || !!customerFeatures?.b2b
    )
  );

  showNotificationChannels$ = combineLatest([this.shoppingCartItems$, this.payer$]).pipe(
    takeUntil(this._orderCompleted),
    map(
      ([items, payer]) =>
        !!payer && items.some((item) => item.features?.orderType === 'Rücklage' || item.features?.orderType === 'Abholung')
    )
  );

  notificationChannel$ = this.applicationService.activatedProcessId$.pipe(
    takeUntil(this._orderCompleted),
    switchMap((processId) => this.domainCheckoutService.getNotificationChannels({ processId }))
  );

  communicationDetails$ = this.applicationService.activatedProcessId$.pipe(
    takeUntil(this._orderCompleted),
    switchMap((processId) => this.domainCheckoutService.getBuyerCommunicationDetails({ processId }))
  );

  notificationChannelLoading$ = new Subject<boolean>();

  showQuantityControlSpinnerItemId: number;
  quantityError$ = new BehaviorSubject<{ [key: string]: string }>({});

  primaryCtaLabel$ = combineLatest([this.payer$, this.shoppingCartItemsWithoutOrderType$]).pipe(
    map(([payer, shoppingCartItemsWithoutOrderType]) => {
      if (shoppingCartItemsWithoutOrderType?.length > 0) {
        return 'Kaufoptionen';
      }
      if (!payer) {
        return 'Weiter';
      }
      return 'Bestellen';
    })
  );

  primaryCtaDisabled$ = this.quantityError$.pipe(
    map((quantityError) => {
      for (const key in quantityError) {
        if (Object.prototype.hasOwnProperty.call(quantityError, key)) {
          return true;
        }
      }
      return this.showOrderButtonSpinner;
    })
  );

  loadingOnItemChangeById$ = new Subject<number>();
  loadingOnQuantityChangeById$ = new Subject<number>();
  showOrderButtonSpinner: boolean;

  constructor(
    private domainCheckoutService: DomainCheckoutService,
    public applicationService: ApplicationService,
    private availabilityService: DomainAvailabilityService,
    private uiModal: UiModalService,
    private auth: AuthService,
    private router: Router,
    private cdr: ChangeDetectorRef,
    private domainCatalogService: DomainCatalogService,
    private breadcrumb: BreadcrumbService,
    private domainPrinterService: DomainPrinterService,
    private _fb: FormBuilder
  ) {
    super({
      shoppingCart: undefined,
      shoppingCartItems: [],
      fetching: false,
    });
  }

  async ngOnInit() {
    this.applicationService.activatedProcessId$.pipe(takeUntil(this._orderCompleted)).subscribe((_) => {
      this.loadShoppingCart();
    });

    await this.removeBreadcrumbs();
    await this.updateBreadcrumb();
    await this.initNotificationsControl();
  }

  loadShoppingCart = this.effect(($) =>
    $.pipe(
      tap(() => (this.fetching = true)),
      withLatestFrom(this.applicationService.activatedProcessId$),
      switchMap(([_, processId]) => {
        return this.domainCheckoutService.getShoppingCart({ processId, latest: true }).pipe(
          tapResponse(
            (shoppingCart) => {
              const shoppingCartItems = shoppingCart?.items?.map((item) => item.data) || [];
              this.patchState({
                shoppingCart,
                shoppingCartItems,
              });
              this.checkQuantityErrors(shoppingCartItems);
            },
            (err) => {},
            () => {}
          )
        );
      }),
      tap(() => (this.fetching = false))
    )
  );

  checkQuantityErrors(shoppingCartItems: ShoppingCartItemDTO[]) {
    shoppingCartItems.forEach((item) => {
      if (item.features?.orderType === 'Rücklage') {
        this.setQuantityError(item, item.availability, item.quantity > item.availability?.inStock);
      } else {
        this.setQuantityError(item, item.availability, false);
      }
    });
  }

  async updateBreadcrumb() {
    await this.breadcrumb.addOrUpdateBreadcrumbIfNotExists({
      key: this.applicationService.activatedProcessId,
      name: 'Warenkorb',
      path: `/kunde/${this.applicationService.activatedProcessId}/cart/review`,
      tags: ['checkout', 'cart'],
      section: 'customer',
    });
  }

  async removeBreadcrumbs() {
    const checkoutDummyCrumbs = await this.breadcrumb
      .getBreadcrumbsByKeyAndTags$(this.applicationService.activatedProcessId, ['checkout', 'cart', 'dummy'])
      .pipe(first())
      .toPromise();
    checkoutDummyCrumbs.forEach(async (crumb) => {
      await this.breadcrumb.removeBreadcrumb(crumb.id, true);
    });
  }

  async initNotificationsControl() {
    const fb = this._fb;
    const notificationChannel = await this.notificationChannel$.pipe(first()).toPromise();
    const communicationDetails = await this.communicationDetails$.pipe(first()).toPromise();
    this.control = fb.group({
      notificationChannel: new FormGroup({
        selected: new FormControl(notificationChannel),
        email: new FormControl(communicationDetails ? communicationDetails.email : '', emailNotificationValidator),
        mobile: new FormControl(communicationDetails ? communicationDetails.mobile : '', mobileNotificationValidator),
      }),
    });
  }

  async onNotificationChange(notificationChannels: NotificationChannel[]) {
    this.notificationChannelLoading$.next(true);

    try {
      const control = this.control?.getRawValue();
      const notificationChannel = notificationChannels.reduce((val, current) => val | current, 0) as NotificationChannel;
      const processId = await this.applicationService.activatedProcessId$.pipe(first()).toPromise();
      const email = control?.notificationChannel?.email;
      const mobile = control?.notificationChannel?.mobile;

      // NotificationChannel nur speichern, wenn Haken und Value gesetzt
      let setNotificationChannel = 0;
      if ((notificationChannel & 1) === 1 && email) {
        setNotificationChannel += 1;
      }
      if ((notificationChannel & 2) === 2 && mobile) {
        setNotificationChannel += 2;
      }

      this.domainCheckoutService.setBuyerCommunicationDetails({ processId, email, mobile });
      this.domainCheckoutService.setNotificationChannels({
        processId,
        notificationChannels: (setNotificationChannel as NotificationChannel) || 0,
      });
    } catch (error) {
      this.uiModal.open({ content: UiErrorModalComponent, data: error, title: 'Fehler beim setzen des Benachrichtigungskanals' });
    }

    this.notificationChannelLoading$.next(false);
  }

  openDummyModal(data?: any) {
    this.uiModal.open({
      content: CheckoutDummyComponent,
      data,
    });
  }

  changeDummyItem({ shoppingCartItem }: { shoppingCartItem: ShoppingCartItemDTO }) {
    const data = {
      price: shoppingCartItem?.availability?.price?.value?.value,
      vat: shoppingCartItem?.availability?.price?.vat?.vatType,
      supplier: shoppingCartItem?.availability?.supplier?.id,
      estimatedShippingDate: shoppingCartItem?.estimatedShippingDate,
      manufacturer: shoppingCartItem?.product?.manufacturer,
      name: shoppingCartItem?.product?.name,
      contributors: shoppingCartItem?.product?.contributors,
      ean: shoppingCartItem?.product?.ean,
      quantity: shoppingCartItem?.quantity,
    };
    this.openDummyModal(data);
  }

  async changeItem({ shoppingCartItem }: { shoppingCartItem: ShoppingCartItemDTO }) {
    this.loadingOnItemChangeById$.next(shoppingCartItem.id);

    const quantity = shoppingCartItem.quantity;

    const branchNo = this.auth.getClaimByKey('branch_no');
    const branchId = shoppingCartItem?.destination?.data?.targetBranch?.id;

    const customerFeatures = await this.customerFeatures$.pipe(first()).toPromise();

    const branch = await this.domainCheckoutService
      .getBranches()
      .pipe(map((branches) => branches.find((branch) => (branchId ? branch.id === branchId : branch.branchNumber === branchNo))))
      .toPromise();

    let catalogItem: ResponseArgsOfItemDTO;
    if (Number.isInteger(shoppingCartItem?.product?.catalogProductNumber)) {
      catalogItem = await this.domainCatalogService
        .getDetailsById({ id: Number(shoppingCartItem.product.catalogProductNumber) })
        .toPromise();
    } else if (shoppingCartItem?.product?.ean) {
      catalogItem = await this.domainCatalogService.getDetailsByEan({ ean: shoppingCartItem.product.ean }).toPromise();
    }

    let takeAwayAvailability: AvailabilityDTO;
    if (!!catalogItem?.result?.product) {
      takeAwayAvailability = await this.availabilityService
        .getTakeAwayAvailability({
          item: {
            itemId: catalogItem.result.id,
            ean: catalogItem.result.product.ean,
            price: catalogItem.result.catalogAvailability?.price,
          },
          quantity,
        })
        .toPromise();
    }

    const pickupAvailability = await this.availabilityService
      .getPickUpAvailability({
        item: {
          itemId: Number(shoppingCartItem.product.catalogProductNumber),
          ean: shoppingCartItem.product.ean,
          price: shoppingCartItem.availability.price,
        },
        branch,
        quantity,
      })
      .toPromise();

    const digAvailability = await this.availabilityService
      .getDigDeliveryAvailability({
        item: {
          itemId: Number(shoppingCartItem.product.catalogProductNumber),
          ean: shoppingCartItem.product.ean,
          price: shoppingCartItem.availability.price,
        },
        quantity,
      })
      .toPromise();

    const b2bAvailability = await this.availabilityService
      .getB2bDeliveryAvailability({
        item: {
          itemId: Number(shoppingCartItem.product.catalogProductNumber),
          ean: shoppingCartItem.product.ean,
          price: shoppingCartItem.availability.price,
        },
        quantity,
      })
      .toPromise();

    const downloadAvailability = await this.availabilityService
      .getDownloadAvailability({
        item: {
          itemId: Number(shoppingCartItem.product.catalogProductNumber),
          ean: shoppingCartItem.product.ean,
          price: shoppingCartItem.availability.price,
        },
      })
      .toPromise();

    let availableOptions: PurchasingOptions[] = [];
    const availabilities: { [key: string]: AvailabilityDTO } = {};

    if (takeAwayAvailability && this.availabilityService.isAvailable({ availability: takeAwayAvailability })) {
      availableOptions.push('take-away');
      availabilities['take-away'] = takeAwayAvailability;
    }

    if (downloadAvailability && this.availabilityService.isAvailable({ availability: downloadAvailability })) {
      availableOptions.push('download');
      availabilities['download'] = downloadAvailability;
    }

    if (pickupAvailability && this.availabilityService.isAvailable({ availability: pickupAvailability })) {
      availableOptions.push('pick-up');
      availabilities['pick-up'] = pickupAvailability;
      if (!customerFeatures?.webshop && this.availabilityService.isAvailable({ availability: b2bAvailability })) {
        availableOptions.push('b2b-delivery');
        availabilities['b2b-delivery'] = b2bAvailability;
      }
    }

    if (digAvailability && this.availabilityService.isAvailable({ availability: digAvailability }) && !customerFeatures?.b2b) {
      availableOptions.push('dig-delivery');
      availabilities['dig-delivery'] = digAvailability;
    }

    if (availableOptions.includes('dig-delivery') && availableOptions.includes('b2b-delivery')) {
      let shippingAvailability = await this.availabilityService
        .getDeliveryAvailability({
          item: {
            itemId: Number(shoppingCartItem.product.catalogProductNumber),
            ean: shoppingCartItem.product.ean,
            price: shoppingCartItem.availability.price,
          },
          quantity,
        })
        .toPromise();
      if (shippingAvailability && this.availabilityService.isAvailable({ availability: shippingAvailability })) {
        availableOptions.push('delivery');
        availabilities['delivery'] = shippingAvailability;
        availableOptions = availableOptions.filter((option) => !(option === 'dig-delivery' || option === 'b2b-delivery'));
      }
    }

    this.loadingOnItemChangeById$.next(undefined);
    this.cdr.markForCheck();

    const itemId = Number(shoppingCartItem.product.catalogProductNumber);
    const modal = this.uiModal.open({
      content: PurchasingOptionsModalComponent,
      data: {
        availableOptions,
        item: {
          id: itemId,
          itemId: itemId,
          product: shoppingCartItem.product,
          price: shoppingCartItem.availability.price,
          catalogAvailability: {
            status: shoppingCartItem.availability.availabilityType,
            price: shoppingCartItem.availability.price,
          },
        },
        shoppingCartItem,
        branchId: branch?.id,
        processId: this.applicationService.activatedProcessId,
        availabilities,
      } as PurchasingOptionsModalData,
    });

    modal.afterClosed$.pipe(takeUntil(this._orderCompleted)).subscribe(() => {
      this.setQuantityError(shoppingCartItem, undefined, false);
    });
  }

  setAgentComment(agentComment: string) {
    this.domainCheckoutService.setSpecialComment({ processId: this.applicationService.activatedProcessId, agentComment });
  }

  async openPrintModal() {
    let shoppingCart = await this.shoppingCart$.pipe(first()).toPromise();
    this.uiModal.open({
      content: PrintModalComponent,
      data: {
        printerType: 'Label',
        print: (printer) => this.domainPrinterService.printCart({ cartId: shoppingCart.id, printer }).toPromise(),
      } as PrintModalData,
      config: {
        panelClass: [],
        showScrollbarY: false,
      },
    });
  }

  async updateItemQuantity({ shoppingCartItem, quantity }: { shoppingCartItem: ShoppingCartItemDTO; quantity: number }) {
    if (quantity === shoppingCartItem.quantity) {
      return;
    }

    this.loadingOnQuantityChangeById$.next(shoppingCartItem.id);

    const shoppingCartItemPrice = shoppingCartItem?.availability?.price?.value?.value;
    const orderType = shoppingCartItem?.features?.orderType;
    let availability: AvailabilityDTO;

    if (quantity) {
      const branch = shoppingCartItem?.destination?.data?.targetBranch?.data;

      if (orderType) {
        switch (orderType) {
          case 'Rücklage':
            availability = await this.availabilityService
              .getTakeAwayAvailability({
                item: {
                  itemId: Number(shoppingCartItem.product.catalogProductNumber),
                  ean: shoppingCartItem.product.ean,
                  price: shoppingCartItem.availability.price,
                },
                quantity,
              })
              .toPromise();

            this.setQuantityError(shoppingCartItem, availability, availability?.inStock < quantity);
            break;
          case 'Abholung':
            availability = await this.availabilityService
              .getPickUpAvailability({
                branch,
                item: {
                  itemId: Number(shoppingCartItem.product.catalogProductNumber),
                  ean: shoppingCartItem.product.ean,
                  price: shoppingCartItem.availability.price,
                },
                quantity,
              })
              .toPromise();
            break;
          case 'Versand':
            availability = await this.availabilityService
              .getDeliveryAvailability({
                item: {
                  itemId: Number(shoppingCartItem.product.catalogProductNumber),
                  ean: shoppingCartItem.product.ean,
                  price: shoppingCartItem.availability.price,
                },
                quantity,
              })
              .toPromise();
            break;
          case 'DIG-Versand':
            availability = await this.availabilityService
              .getDigDeliveryAvailability({
                item: {
                  itemId: Number(shoppingCartItem.product.catalogProductNumber),
                  ean: shoppingCartItem.product.ean,
                  price: shoppingCartItem.availability.price,
                },
                quantity,
              })
              .toPromise();
            break;
          case 'B2B-Versand':
            availability = await this.availabilityService
              .getB2bDeliveryAvailability({
                item: {
                  itemId: Number(shoppingCartItem.product.catalogProductNumber),
                  ean: shoppingCartItem.product.ean,
                  price: shoppingCartItem.availability.price,
                },
                quantity,
              })
              .toPromise();
            break;
          case 'Download':
            availability = await this.availabilityService
              .getDownloadAvailability({
                item: {
                  itemId: Number(shoppingCartItem.product.catalogProductNumber),
                  ean: shoppingCartItem.product.ean,
                  price: shoppingCartItem.availability.price,
                },
              })
              .toPromise();
            break;
        }
      }
    }

    if (quantity === 0) {
      await this.domainCheckoutService
        .updateItemInShoppingCart({
          processId: this.applicationService.activatedProcessId,
          shoppingCartItemId: shoppingCartItem.id,
          update: {
            quantity,
            availability: null,
          },
        })
        .toPromise();
      this.setQuantityError(shoppingCartItem, availability, false);
    } else if (availability) {
      await this.domainCheckoutService
        .updateItemInShoppingCart({
          processId: this.applicationService.activatedProcessId,
          shoppingCartItemId: shoppingCartItem.id,
          update: {
            quantity,
            availability: this.compareDeliveryAndCatalogPrice(availability, orderType, shoppingCartItemPrice),
          },
        })
        .toPromise();
    } else {
      await this.domainCheckoutService
        .updateItemInShoppingCart({
          processId: this.applicationService.activatedProcessId,
          shoppingCartItemId: shoppingCartItem.id,
          update: {
            quantity,
          },
        })
        .toPromise();
    }

    this.loadingOnQuantityChangeById$.next(undefined);
  }

  setQuantityError(item: ShoppingCartItemDTO, availability: AvailabilityDTO, error: boolean) {
    const quantityErrors: { [key: string]: string } = this.quantityError$.value;
    if (error) {
      quantityErrors[item.product.catalogProductNumber] = `${availability.inStock} Exemplar(e) sofort lieferbar`;
      this.quantityError$.next({ ...quantityErrors });
    } else {
      delete quantityErrors[item.product.catalogProductNumber];
      this.quantityError$.next({ ...quantityErrors });
    }
  }

  // Bei unbekannten Kunden und DIG Bestellung findet ein Vergleich der Preise statt
  compareDeliveryAndCatalogPrice(availability: AvailabilityDTO, orderType: string, shoppingCartItemPrice: number) {
    if (['Versand', 'DIG-Versand'].includes(orderType) && shoppingCartItemPrice < availability?.price?.value?.value) {
      return {
        ...availability,
        price: {
          ...availability.price,
          value: {
            ...availability.price.value,
            value: shoppingCartItemPrice,
          },
        },
      };
    }
    return availability;
  }

  async changeAddress() {
    const processId = this.applicationService.activatedProcessId;
    const customer = await this.domainCheckoutService.getBuyer({ processId }).pipe(first()).toPromise();
    if (!customer) {
      this.navigateToCustomerSearch(processId);
      return;
    }
    const customerId = customer.source;
    this.router.navigate(['/kunde', this.applicationService.activatedProcessId, 'customer', `${customerId}`]);
  }

  async navigateToCustomerSearch(processId: number) {
    try {
      const response = await this.customerFeatures$
        .pipe(
          first(),
          switchMap((customerFeatures) => {
            return this.domainCheckoutService.canSetCustomer({ processId, customerFeatures });
          })
        )
        .toPromise();
      this.router.navigate(['/kunde', this.applicationService.activatedProcessId, 'customer', 'search'], {
        queryParams: { filter_customertype: response.filter.customertype },
      });
    } catch (error) {
      this.router.navigate(['/kunde', this.applicationService.activatedProcessId, 'customer', 'search']);
    }
  }

  async showPurchasingListModal(shoppingCartItems: ShoppingCartItemDTO[]) {
    const customerFeatures = await this.customerFeatures$.pipe(first()).toPromise();
    this.uiModal.open({
      content: PurchasingOptionsListModalComponent,
      title: 'Wie möchten Sie die Artikel erhalten?',
      config: { showScrollbarY: false },
      data: {
        processId: this.applicationService.activatedProcessId,
        shoppingCartItems: shoppingCartItems,
        customerFeatures,
      } as PurchasingOptionsListModalData,
    });
  }

  async order() {
    const shoppingCartItemsWithoutOrderType = await this.shoppingCartItemsWithoutOrderType$.pipe(first()).toPromise();

    if (shoppingCartItemsWithoutOrderType?.length > 0) {
      this.showPurchasingListModal(shoppingCartItemsWithoutOrderType);
      return;
    }

    const processId = this.applicationService.activatedProcessId;
    const customer = await this.domainCheckoutService.getBuyer({ processId }).pipe(first()).toPromise();
    if (!customer) {
      this.navigateToCustomerSearch(processId);
    } else {
      try {
        this.showOrderButtonSpinner = true;
        const orders = await this.domainCheckoutService.completeCheckout({ processId }).toPromise();
        const orderIds = orders.map((order) => order.id).join(',');
        this._orderCompleted.next();
        await this.patchProcess(processId);
        await this.router.navigate(['/kunde', processId, 'cart', 'summary', orderIds]);
      } catch (error) {
        const response = error?.error;
        let message: string = response?.message ?? '';

        if (response?.invalidProperties && Object.values(response?.invalidProperties)?.length) {
          message += `\n${Object.values(response.invalidProperties).join('\n')}`;
        }

        if (message?.length) {
          this.uiModal.open({
            content: UiMessageModalComponent,
            title: 'Hinweis',
            data: { message: message.trim() },
          });
        }

        if (error.status === 409) {
          this._orderCompleted.next();
          await this.patchProcess(processId);
          await this.router.navigate(['/kunde', processId, 'cart', 'summary']);
        }
      } finally {
        this.showOrderButtonSpinner = false;
        this.cdr.markForCheck();
      }
    }
  }

  async patchProcess(processId: number) {
    this.applicationService.patchProcess(processId, {
      type: 'cart-checkout',
    });
  }
}
