import { ChangeDetectionStrategy, Component, ViewChild } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import {
  map,
  switchMap,
  first,
  filter,
  flatMap,
  distinctUntilChanged,
  shareReplay,
  take,
  takeUntil,
  withLatestFrom,
} from 'rxjs/operators';
import {
  OrderItemProcessingStatusValue,
  OrderItemListItemDTO,
  StatusValues,
  ResponseArgsOfValueTupleOfOrderItemSubsetDTOAndOrderItemSubsetDTO,
} from '@swagger/oms';
import { ShelfNavigationService } from '../../shared/services';
import { race, combineLatest } from 'rxjs';
import { AppService } from '@sales/core-services';
import { OMSPrintService, PrintService } from '@swagger/print';
import { PrinterSelectionComponent } from 'apps/sales/src/app/components/printer-selection/printer-selection.component';
import { OrderDetailsCardInput } from '../../components/order-details-card';
import { DetailsFacade } from '@shelf-store/details';
import { ProcessingStatusNameMap } from '../../constants';

@Component({
  selector: 'app-shelf-order-details',
  templateUrl: './shelf-order-details.component.html',
  styleUrls: ['./shelf-order-details.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ShelfOrderDetailsComponent {
  @ViewChild(PrinterSelectionComponent, {
    read: PrinterSelectionComponent,
    static: true,
  })
  printerSlectionComponent: PrinterSelectionComponent;

  processingStatus$ = this.activatedRoute.params.pipe(
    map((params) => params['processingStatus'])
  );

  partialPickup = false;

  selectedForPartialPickup = new Map<number, OrderItemListItemDTO>();

  quantityForPartialPickup = new Map<number, number>();

  orderNumber$ = this.activatedRoute.params.pipe(
    map((params) => params['orderId']),
    filter((v) => !!v),
    distinctUntilChanged()
  );

  compartmentCode$ = this.activatedRoute.params.pipe(
    map((params) => params['compartmentCode']),
    filter((v) => !!v),
    distinctUntilChanged()
  );

  orderItems$ = race(
    this.orderNumber$.pipe(
      switchMap((orderNumber) =>
        this.detailsFacade.getOrderItemsByOrderNumber$(orderNumber)
      )
    ),
    this.compartmentCode$.pipe(
      switchMap((compartmentCode) =>
        this.detailsFacade.getOrderItemsByCompartmentCode$(compartmentCode)
      )
    )
  ).pipe(
    flatMap((items) =>
      this.processingStatus$.pipe(
        map((processingStatus) => {
          if (!!processingStatus) {
            return items.filter(
              (item) => item.processingStatus === +processingStatus
            );
          }
          return items;
        })
      )
    ),
    shareReplay(1)
  );

  orderDetailsCard$ = this.orderItems$.pipe(
    map((items) => {
      if (items.length > 0) {
        const item = items[0];
        return {
          firstName: item.firstName,
          lastName: item.lastName,
          customerNumber: item.buyerNumber,
          estimatedShippingDate: item.estimatedShippingDate
            ? new Date(item.estimatedShippingDate)
            : undefined,
          orderDate: new Date(item.orderDate),
          orderNumber: item.orderNumber,
          processingStatus: item.processingStatus,
          processingStatusDate: new Date(item.processingStatusDate),
          orderChannel: item.clientChannel,
          compartmentCode: item.compartmentCode,
          compartmentInfo: item.compartmentInfo,
          pickupDeadline: new Date(item.pickUpDeadline),
        } as OrderDetailsCardInput;
      }
      return undefined;
    }),
    filter((details) => !!details)
  );

  showArrivedAndPrintCta$ = this.orderDetailsCard$.pipe(
    map(
      (details) =>
        details.processingStatus === 16 || details.processingStatus === 8192
    ) // wenn bestellt(=16) oder nachbestellt(=8192)
  );

  showArrivedCta$ = this.orderDetailsCard$.pipe(
    map((details) => details.processingStatus === 256)
  ); // wenn abgeholt(=256)

  showPickUpCta$ = this.orderDetailsCard$.pipe(
    map((details) => details.processingStatus === 128) // wenn eingetroffen(=128)
  );

  showPickUpPartialCollectCta$ = combineLatest([
    this.orderDetailsCard$,
    this.orderItems$,
  ]).pipe(
    map(([details, items]) => {
      return (
        details.processingStatus === 128 &&
        (items.length > 1 || items.some((i) => i.quantity > 1))
      );
    }) // wenn eingetroffen(=128) und mehr als 1 Artikel
  );

  showBackToStoreCta$ = this.orderDetailsCard$.pipe(
    map(
      (details) =>
        details.processingStatus === 1024 || details.processingStatus === 512
    )
  );

  showAddToRemissionListCta$ = this.orderDetailsCard$.pipe(
    map(
      (details) =>
        details.processingStatus === 1024 || details.processingStatus === 512
    )
  );

  showReOrderCta$ = this.orderDetailsCard$.pipe(
    map(
      (details) =>
        details.processingStatus === 2048 &&
        !ProcessingStatusNameMap.get(8192).disabled
    )
  );

  constructor(
    private activatedRoute: ActivatedRoute,
    private detailsFacade: DetailsFacade,
    private shelfNavigationService: ShelfNavigationService,
    private omsPrintService: OMSPrintService,
    private printerService: PrintService,
    private appService: AppService
  ) {}

  selectOrderItem(orderItem: OrderItemListItemDTO, value: boolean) {
    if (!value) {
      this.selectedForPartialPickup.delete(orderItem.orderItemId);
    } else {
      this.selectedForPartialPickup.set(orderItem.orderItemId, orderItem);
    }
  }

  setOrderItemQuanity(orderItem: OrderItemListItemDTO, quantity: number) {
    this.quantityForPartialPickup.set(orderItem.orderItemId, quantity);
  }

  saveComment(item: OrderItemListItemDTO, comment: string) {
    this.detailsFacade.setComment({
      item,
      comment,
    });
  }

  async changeProcessingStatus(
    status: OrderItemProcessingStatusValue,
    data: { compartmentInfo?: string; shouldPrint?: boolean } = {}
  ) {
    let items = await this.orderItems$.pipe(first()).toPromise();
    let results: ResponseArgsOfValueTupleOfOrderItemSubsetDTOAndOrderItemSubsetDTO[];
    const navigate = this.partialPickup
      ? items.length === this.selectedForPartialPickup.size
      : true;
    if (status === 256) {
      if (this.partialPickup) {
        items = items.filter(({ orderItemId }) =>
          this.selectedForPartialPickup.has(orderItemId)
        );
      }
      results = await this.detailsFacade.pickedUp(
        items,
        this.quantityForPartialPickup
      );
    } else if (status === 262144) {
      results = await this.detailsFacade.backToStock(items);
    } else if (status === 8192) {
      results = await this.detailsFacade.reorder(items);
    } else if (status === 128) {
      results = await this.detailsFacade.arrived(items, data.compartmentInfo);
      if (data.shouldPrint) {
        await this.printAbholfachetikett(
          items.map(({ orderItemSubsetId }) => orderItemSubsetId)
        );
      }
    } else {
      const payloads = items.map(
        ({ orderId, orderItemId, orderItemSubsetId }) => ({
          orderId,
          orderItemId,
          orderItemSubsetId,
          data: { processingStatus: status } as StatusValues,
        })
      );
      results = await this.detailsFacade.changeStatus(payloads);
    }

    this.partialPickup = false;
    this.selectedForPartialPickup.clear();
    this.quantityForPartialPickup.clear();

    if (navigate) {
      const firstItem = items[0];
      const firstResult = results[0].result;
      this.shelfNavigationService.updateDetails(
        {
          compartmentCode: firstResult.item1.compartmentCode,
          orderNumber: firstItem.orderNumber,
          processingStatus: firstResult.item1.processingStatus,
        },
        {
          compartmentCode: firstItem.compartmentCode,
          orderNumber: firstItem.orderNumber,
          processingStatus: firstItem.processingStatus,
        }
      );
    }
  }

  changeEstimatedShippingDate(estimatedShippingDate: Date | string) {
    this.orderItems$.pipe(first()).subscribe((items) => {
      this.detailsFacade.setEstimatedShippingDate({
        items,
        estimatedShippingDate,
      });
    });
  }

  changePickupDeadline(pickUpDeadline: Date | string) {
    this.orderItems$.pipe(first()).subscribe((items) => {
      this.detailsFacade.setPickUpDeadline({ items, pickUpDeadline });
    });
  }

  async printAbholfachetikett(data: number[]) {
    // tslint:disable-next-line: no-shadowed-variable
    const print = (printer?: string) =>
      this.omsPrintService
        .OMSPrintAbholfachetikettResponse({
          data,
          printer,
        })
        .toPromise();

    let printer: string;

    if (this.appService.isIPadEnv()) {
      this.printerSlectionComponent.openDialog();
      printer = await this.printerSlectionComponent.print
        .pipe(takeUntil(this.printerSlectionComponent.closed), take(1))
        .toPromise();
      this.printerSlectionComponent.closeModal();
    }

    if (!printer) {
      printer = await this.getDefaultPrinter();
    }

    try {
      print(printer);
    } catch (error) {
      console.log('Printing Abholfachetikett Failed');
    }
  }

  async getDefaultPrinter(): Promise<string> {
    const printers = await this.printerService.PrintLabelPrinters().toPromise();
    const selected = printers.result.find((f) => f.selected);

    if (!!selected) {
      return selected.key;
    }

    return undefined;
  }

  activatePartialPickup() {
    this.partialPickup = true;
  }

  async navigateToDetails() {
    const data = await race(
      this.orderNumber$.pipe(map((orderNumber) => ({ orderNumber }))),
      this.compartmentCode$.pipe(
        map((compartmentCode) => ({ compartmentCode }))
      )
    )
      .pipe(
        withLatestFrom(this.processingStatus$),
        map(([d, processingStatus]) => ({ ...d, processingStatus })),
        first()
      )
      .toPromise();

    this.shelfNavigationService.navigateToEdit(data);
  }

  navigateToHistory(orderItem: OrderItemListItemDTO) {
    this.shelfNavigationService.navigateToHistory(orderItem);
  }

  async selectAllForPickUp() {
    const items = await this.orderItems$.pipe(first()).toPromise();
    items.forEach((item) => this.selectOrderItem(item, true));
  }

  async unselectAllForPickUp() {
    const items = await this.orderItems$.pipe(first()).toPromise();
    items.forEach((item) => this.selectOrderItem(item, false));
  }
}
