import {
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  Injector,
  OnDestroy,
  OnInit,
  inject,
  computed,
} from '@angular/core';
import { DomainCheckoutService } from '@domain/checkout';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
import { PrintModalComponent, PrintModalData } from '@modal/printer';
import { first, map, shareReplay, switchMap } from 'rxjs/operators';
import { CrmCustomerService } from '@domain/crm';
import { ActivatedRoute, Router } from '@angular/router';
import { DomainOmsService } from '@domain/oms';
import { DomainCatalogService } from '@domain/catalog';
import {
  DisplayOrderDTO,
  DisplayOrderItemDTO,
  DisplayOrderItemSubsetDTO,
} from '@generated/swagger/oms-api';
import { BreadcrumbService } from '@core/breadcrumb';
import { ApplicationService } from '@core/application';
import { DomainPrinterService } from '@domain/printer';
import { BehaviorSubject, combineLatest, NEVER, Subject } from 'rxjs';
import { DateAdapter } from '@ui/common';
import {
  CheckoutNavigationService,
  PickUpShelfOutNavigationService,
  ProductCatalogNavigationService,
} from '@shared/services/navigation';
import { EnvironmentService } from '@core/environment';
import { SendOrderConfirmationModalService } from '@modal/send-order-confirmation';
import { ToasterService } from '@shared/shell';
import { SelectedRewardShoppingCartResource } from '@isa/checkout/data-access';
import { injectTabId } from '@isa/core/tabs';
import { PrimaryCustomerCardResource } from '@isa/crm/data-access';

@Component({
  selector: 'page-checkout-summary',
  templateUrl: 'checkout-summary.component.html',
  styleUrls: ['checkout-summary.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  standalone: false,
})
export class CheckoutSummaryComponent implements OnInit, OnDestroy {
  private _injector = inject(Injector);

  #tabId = injectTabId();
  #primaryCustomerCardResource = inject(PrimaryCustomerCardResource);
  #rewardShoppingCartResource = inject(SelectedRewardShoppingCartResource)
    .resource;

  readonly rewardShoppingCartResponseValue =
    this.#rewardShoppingCartResource.value.asReadonly();
  readonly primaryCustomerCardValue =
    this.#primaryCustomerCardResource.primaryCustomerCard;

  displayRewardNavigation = computed(() => {
    const rewardShoppingCart = this.rewardShoppingCartResponseValue();
    const hasPrimaryCard = this.primaryCustomerCardValue();
    return !!rewardShoppingCart?.items?.length && hasPrimaryCard;
  });

  get sendOrderConfirmationModalService() {
    return this._injector.get(SendOrderConfirmationModalService);
  }

  private _toaster = inject(ToasterService);

  private _onDestroy$ = new Subject<void>();
  processId = Date.now();
  selectedDate = this.dateAdapter.today();
  minDateDatepicker = this.dateAdapter.addCalendarDays(
    this.dateAdapter.today(),
    -1,
  );

  updatingPreferredPickUpDate$ = new BehaviorSubject<Record<string, string>>(
    {},
  );

  displayOrders$ = combineLatest([
    this.domainCheckoutService.getOrders(),
    this._route.params,
  ]).pipe(
    map(([orders, params]) => {
      let filteredOrders: DisplayOrderDTO[] = [];
      if (params?.orderIds) {
        const orderIds: string[] = params.orderIds.split(',');
        filteredOrders = orders.filter((order) =>
          orderIds.find((id) => Number(id) === order.id),
        );
      } else {
        return filteredOrders;
      }

      // Ticket #4228 Für die korrekte Gruppierung der Items bei gleichem Bestellziel (Aufsplitten von Abholung und Rücklage)
      const ordersWithMultipleFeatures = filteredOrders.filter((order) =>
        order.items.find(
          (item) => item.features.orderType !== order.features.orderType,
        ),
      );

      if (ordersWithMultipleFeatures) {
        for (const orderWithMultipleFeatures of ordersWithMultipleFeatures) {
          if (orderWithMultipleFeatures?.items?.length > 1) {
            const itemsWithOrderFeature =
              orderWithMultipleFeatures.items.filter(
                (item) =>
                  item.features.orderType ===
                  orderWithMultipleFeatures.features.orderType,
              );
            const itemsWithDifferentOrderFeature =
              orderWithMultipleFeatures.items.filter(
                (item) =>
                  item.features.orderType !==
                  orderWithMultipleFeatures.features.orderType,
              );

            filteredOrders = [
              ...filteredOrders.filter(
                (order) => order.id !== orderWithMultipleFeatures.id,
              ),
            ];

            if (itemsWithOrderFeature?.length > 0) {
              filteredOrders = [
                ...filteredOrders,
                { ...orderWithMultipleFeatures, items: itemsWithOrderFeature },
              ];
            }

            if (itemsWithDifferentOrderFeature?.length > 0) {
              filteredOrders = [
                ...filteredOrders,
                {
                  ...orderWithMultipleFeatures,
                  items: itemsWithDifferentOrderFeature,
                },
              ];
            }
          }
        }
      }

      return filteredOrders?.map((order) => {
        return {
          ...order,
          items: [...order.items]?.sort((a, b) =>
            a.product?.name.localeCompare(b.product?.name),
          ),
        };
      });
    }),
    shareReplay(),
  );

  hasAbholung$ = this.displayOrders$.pipe(
    map(
      (displayOrders) =>
        displayOrders.filter(
          (order) => order.features?.orderType === 'Abholung',
        )?.length > 0,
    ),
  );

  totalItemCount$ = this.displayOrders$.pipe(
    map((displayOrders) =>
      displayOrders.reduce(
        (total, displayOrder) =>
          total +
          displayOrder?.items?.reduce(
            (subTotal, order) => subTotal + order?.quantity,
            0,
          ),
        0,
      ),
    ),
  );

  totalReadingPoints$ = this.displayOrders$.pipe(
    switchMap((displayOrders) => {
      const items = displayOrders
        .reduce<DisplayOrderItemDTO[]>(
          (items, order) => [...items, ...order.items],
          [],
        )
        .map((i) => {
          if (i?.product?.catalogProductNumber) {
            return {
              id: Number(i.product?.catalogProductNumber),
              quantity: i.quantity,
              price: i.price?.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;
      }
    }),
  );

  totalPrice$ = this.displayOrders$.pipe(
    map((displayOrders) =>
      displayOrders.reduce(
        (total, displayOrder) =>
          total +
          displayOrder?.items?.reduce(
            (subTotal, order) =>
              subTotal + order?.price?.value?.value * order.quantity,
            0,
          ),
        0,
      ),
    ),
  );

  isPrinting$ = new BehaviorSubject(false);

  totalPriceCurrency$ = this.displayOrders$.pipe(
    map((displayOrders) => displayOrders[0]?.items[0]?.price?.value?.currency),
  );

  containsDeliveryOrder$ = this.displayOrders$.pipe(
    map(
      (displayOrders) =>
        displayOrders.filter(
          (o) =>
            ['Versand', 'B2B-Versand', 'DIG-Versand'].indexOf(
              o.features?.orderType,
            ) > -1,
        )?.length > 0,
    ),
  );

  customer$ = this.displayOrders$.pipe(
    switchMap((o) =>
      this.customerService.getCustomers(o[0].buyerNumber, { take: 5 }),
    ),
    map((customers) => customers.result[0]),
    shareReplay(),
  );

  isB2BCustomer$ = this.customer$.pipe(
    map((customer) => customer?.features?.find((f) => f.key === 'b2b') != null),
  );

  takeNowOrders$ = this.displayOrders$.pipe(
    map((displayOrders) =>
      displayOrders.filter(
        (o) =>
          o.items.find((oi) => oi.features?.orderType === 'Rücklage') != null,
      ),
    ),
  );

  get isDesktop$() {
    return this._environmentService.matchDesktopLarge$;
  }

  get isTablet() {
    return this._environmentService.matchTablet();
  }

  expanded: boolean[] = [];

  constructor(
    private domainCheckoutService: DomainCheckoutService,
    private customerService: CrmCustomerService,
    private domainCatalogService: DomainCatalogService,
    private router: Router,
    private _route: ActivatedRoute,
    private omsService: DomainOmsService,
    private uiModal: UiModalService,
    private breadcrumb: BreadcrumbService,
    public applicationService: ApplicationService,
    private domainPrinterService: DomainPrinterService,
    private dateAdapter: DateAdapter,
    private _navigation: CheckoutNavigationService,
    private _productNavigationService: ProductCatalogNavigationService,
    private _shelfOutNavigationService: PickUpShelfOutNavigationService,
    private _environmentService: EnvironmentService,
    private _cdr: ChangeDetectorRef,
  ) {
    this.breadcrumb
      .getBreadcrumbsByKeyAndTags$(this.applicationService.activatedProcessId, [
        'checkout',
      ])
      .pipe(first())
      .subscribe(async (crumbs) => {
        for await (const crumb of crumbs) {
          this.breadcrumb.removeBreadcrumb(crumb.id);
        }
        this.breadcrumb.addBreadcrumbIfNotExists({
          key: this.applicationService.activatedProcessId,
          name: 'Bestellbestätigung',
          path: this._navigation.getCheckoutSummaryPath({
            processId: this.applicationService.activatedProcessId,
            orderIds: this._route.snapshot.params.orderIds,
          }).path,
          tags: ['checkout', 'cart'],
          section: 'customer',
        });
      });
  }

  async ngOnInit() {
    const displayOrders = await this.displayOrders$.pipe(first()).toPromise();
    if (displayOrders?.length === 1) {
      this.expanded = [true];
    } else {
      this.expanded = [];
    }
    this._cdr.markForCheck();
  }

  async ngOnDestroy() {
    const checkoutProcess = await this.applicationService
      .getLastActivatedProcessWithSectionAndType$('customer', 'cart-checkout')
      .pipe(first())
      .toPromise();

    // Wenn es keine Bestellabschluss Prozesse mehr gibt, werden alle Orders aus dem Store removed um die Performance zu verbessern
    if (!checkoutProcess) {
      this.domainCheckoutService.removeAllOrders();
    }

    this._onDestroy$.next();
    this._onDestroy$.complete();
  }

  getProductSearchDetailsPath(ean: string) {
    return this._productNavigationService.getArticleDetailsPathByEan({
      processId: this.processId,
      ean,
    }).path;
  }

  getProductSearchDetailsQueryParams(item: DisplayOrderItemDTO) {
    return {
      main_qs: item?.product?.ean,
      filter_format:
        item?.features?.orderType === 'Download' ? 'eb;dl' : undefined,
    };
  }

  async updatePreferredPickUpDate(item: DisplayOrderItemDTO, date: Date) {
    const data: Record<string, string> = {};

    try {
      const items = item ? [item] : await this.getAllOrderItems();
      const subsetItems = items
        .filter((item) =>
          ['Rücklage', 'Abholung'].includes(item.features.orderType),
        )
        // .flatMap((item) => item.subsetItems);
        .reduce<DisplayOrderItemSubsetDTO[]>(
          (acc, item) => [...acc, ...item.subsetItems],
          [],
        );
      subsetItems.forEach((item) => (data[`${item.id}`] = date?.toISOString()));

      try {
        this.updatingPreferredPickUpDate$.next(data);
        await this.omsService.setPreferredPickUpDate({ data }).toPromise();
      } catch (error) {
        this.uiModal.open({
          content: UiErrorModalComponent,
          title: 'Fehler beim setzen des Wunschdatums',
          data: error,
        });
      } finally {
        this.updatingPreferredPickUpDate$.next({});
      }

      items.forEach((item) => {
        this.updateDisplayOrderItem({
          ...item,
          subsetItems: subsetItems.map((subsetItem) => {
            return {
              ...subsetItem,
              preferredPickUpDate: date?.toISOString(),
            };
          }),
        });
      });
    } catch (error) {
      console.error(error);
    }
  }

  async getAllOrderItems() {
    const orders = await this.displayOrders$.pipe(first()).toPromise();
    return orders.reduce<DisplayOrderItemDTO[]>(
      (agg, order) => [...agg, ...order.items],
      [],
    );
  }

  async updateDisplayOrderItem(item: DisplayOrderItemDTO) {
    this.domainCheckoutService.updateOrderItem(item);
  }

  async navigateToShelfOut() {
    const takeNowOrders = await this.takeNowOrders$.pipe(first()).toPromise();
    if (takeNowOrders.length != 1) return;

    try {
      await this.router.navigate(
        this._shelfOutNavigationService.listRoute({ processId: Date.now() })
          .path,
        {
          queryParams: {
            main_qs: takeNowOrders[0].orderNumber,
            filter_supplier_id: '16',
          },
        },
      );
    } catch (e) {
      console.error(e);
    }
  }

  async sendOrderConfirmation() {
    const orders = await this.displayOrders$.pipe(first()).toPromise();

    await this.sendOrderConfirmationModalService.open(orders);
  }

  async navigateToReward() {
    await this.router.navigate([`/${this.#tabId()}`, 'reward', 'cart']);
  }

  async printOrderConfirmation() {
    this.isPrinting$.next(true);
    const orders = await this.displayOrders$.pipe(first()).toPromise();
    const selectedPrinter = await this.domainPrinterService
      .getAvailableLabelPrinters()
      .pipe(
        first(),
        map((printers) => {
          if (Array.isArray(printers))
            return printers.find((printer) => printer.selected === true);
        }),
      )
      .toPromise();

    console.log(selectedPrinter);
    if (!selectedPrinter || this.isTablet) {
      await this.uiModal
        .open({
          content: PrintModalComponent,
          data: {
            printerType: 'Label',
            printImmediately: !this.isTablet,
            print: async (printer) => {
              try {
                const result = await this.domainPrinterService
                  .printOrder({ orderIds: orders.map((o) => o.id), printer })
                  .toPromise();
                this._toaster.open({
                  type: 'success',
                  message: 'Bestellbestätigung wurde gedruckt',
                });
                return result;
              } catch (error) {
                this._toaster.open({
                  type: 'danger',
                  message: 'Fehler beim Drucken der Bestellbestätigung',
                });
              } finally {
                this.isPrinting$.next(false);
              }
            },
          } as PrintModalData,
          config: {
            panelClass: [],
            showScrollbarY: false,
          },
        })
        .afterClosed$.toPromise();
      this.isPrinting$.next(false);
    } else {
      try {
        const result = await this.domainPrinterService
          .printOrder({
            orderIds: orders.map((o) => o.id),
            printer: selectedPrinter.key,
          })
          .toPromise();
        this._toaster.open({
          type: 'success',
          message: 'Bestellbestätigung wurde gedruckt',
        });
        return result;
      } catch (error) {
        this._toaster.open({
          type: 'danger',
          message: 'Fehler beim Drucken der Bestellbestätigung',
        });
      } finally {
        this.isPrinting$.next(false);
      }
    }
  }
}
