import {
  Component,
  OnInit,
  OnDestroy,
  ViewChild,
  ElementRef,
  AfterViewInit,
  ChangeDetectorRef,
  HostListener,
  ViewRef,
} from '@angular/core';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { take, switchMap, filter, takeUntil, concatMap, catchError, map } from 'rxjs/operators';
import { of, Observable, Subject } from 'rxjs';
import { CollectingShelfService } from 'apps/sales/src/app/core/services/collecting-shelf.service';
import { isNullOrUndefined } from 'util';
import { OrderDTO, OrderItemDTO, OrderItemSubsetDTO } from '@swagger/oms';
import { orderStatusMapper, orderChannelMapper, OrderStatus } from 'apps/sales/src/app/core/mappings/shelf.mapping';
import { Store } from '@ngxs/store';
import { SetBranchProcessCurrentPath } from 'apps/sales/src/app/core/store/actions/branch-process.actions';
import { AddBreadcrumb } from 'apps/sales/src/app/core/store/actions/breadcrumb.actions';
import { Breadcrumb } from 'apps/sales/src/app/core/models/breadcrumb.model';
import { SetEditOrder } from 'apps/sales/src/app/core/store/actions/collecting-shelf.action';
import { PrinterService } from 'apps/sales/src/app/core/services/printer.service';
import { AppService } from 'apps/sales/src/app/core/services/app.service';

enum Tag {
  Maxi = 0,
  Mini = 1,
  SmallThings = 2,
  NonBooks = 3,
  Custom = 4,
}

export const tagsValues: number[] = [0, 1, 2, 3, 4];

@Component({
  selector: 'app-goods-in-order-details',
  templateUrl: './goods-in-order-details.component.html',
  styleUrls: ['./goods-in-order-details.component.scss'],
})
export class GoodsInOrderDetailsComponent implements OnInit, OnDestroy, AfterViewInit {
  orderId: number;
  itemId: number;
  processingStatus: number;
  order: OrderDTO;
  allItems: OrderItemDTO[] = [];
  expand = {};
  esitmatedShippingDate: Date;
  orderChannel: string;
  positiveStatuses = {
    16: true,
    128: true,
    8192: true,
  };
  negativeStatuses = {
    512: true,
    1024: true,
    2048: true,
    4096: true,
    16777216: true,
  };
  isPositiveStatus = false;
  isNegativeStatus = false;
  destroy$ = new Subject();
  tags: {
    [tag: number]: { label?: string; selected: boolean };
  } = {
    0: { selected: false },
    1: { selected: false },
    2: { selected: false },
    3: { selected: false },
    4: { selected: false },
  };
  labelChecked = false;
  notAvailableModel = {
    comment: { value: '', disabled: true, errors: undefined },
    message: { code: '', value: '', valid: false, disabled: true, errors: undefined },
  };
  isIPad = false;

  // attibutes for horizontal navigational arrow of all items
  @ViewChild('allItemsEL') allItemsEL: ElementRef;
  showNext = false;
  showBack = false;
  showFowardContainer = false;
  showBackContainer = false;
  offset = 0;
  topPosition = 0;
  // --end--

  constructor(
    private route: ActivatedRoute,
    private shelfService: CollectingShelfService,
    private router: Router,
    private store: Store,
    private printerService: PrinterService,
    private appService: AppService,
    private cdr: ChangeDetectorRef
  ) {}

  get status() {
    if (this.processingStatus) {
      return orderStatusMapper[this.processingStatus];
    }
  }

  ngOnInit() {
    this.isIPad = this.appService.isIPadEnv();
    this.initialize();
  }

  ngAfterViewInit() {
    if (!this.isIPad) {
      setTimeout(() => {
        this.calculateTopPosition();
        this.initShowArrows();
      }, 1200);
    }
  }

  ngOnDestroy() {
    this.destroy$.next();
  }

  initialize() {
    this.route.params
      .pipe(
        take(1),
        switchMap(this.paramsToOrderSwitcher),
        filter((order) => !isNullOrUndefined(order))
      )
      .subscribe(this.orderResultHandler);
  }

  paramsToOrderSwitcher = (params: Params) => {
    if (params && params['id'] && params['itemId'] && params['status']) {
      this.orderId = params['id'];
      this.itemId = params['itemId'];
      this.processingStatus = params['status'];
      return this.shelfService.getOrderByOrderId(this.orderId);
    }
    return of(undefined);
  };

  orderResultHandler = (order: OrderDTO) => {
    this.order = this.filterOrder(order);
    if (
      this.order.items[0] &&
      this.order.items[0].data.subsetItems[0] &&
      this.order.items[0].data.subsetItems[0].data.estimatedShippingDate
    ) {
      this.esitmatedShippingDate = new Date(this.order.items[0].data.subsetItems[0].data.estimatedShippingDate);
    }
    if (this.processingStatus) {
      this.isPositiveStatus = this.positiveStatuses[this.processingStatus] ? true : false;
      this.isNegativeStatus = this.negativeStatuses[this.processingStatus] ? true : false;
    }
    if (this.order.clientChannel) {
      this.orderChannel = orderChannelMapper[this.order.clientChannel];
    }
    if (this.order.items[0] && this.order.items[0].data.subsetItems[0] && this.order.items[0].data.subsetItems[0].data.specialComment) {
      this.notAvailableModel.comment.value = this.order.items[0].data.subsetItems[0].data.specialComment;
    }
  };

  filterOrder(orderDTO: OrderDTO) {
    let order = orderDTO;
    if (order) {
      this.allItems = order.items.map((i) => i.data);
      order = {
        ...order,
        items: [
          ...order.items
            .map((item) => {
              const filteredItemsByStatus = {
                ...item.data,
                subsetItems: [
                  ...item.data.subsetItems
                    .map((subitem) => {
                      if (subitem && subitem.data && subitem.data.processingStatus === +this.processingStatus) {
                        return subitem;
                      }
                    })
                    .filter((f) => f && f.data),
                ],
              };
              const subitems = {
                ...item,
                data: filteredItemsByStatus,
              };
              return subitems;
            })
            .filter((f) => f && f.data && f.id === +this.itemId && f.data.subsetItems && f.data.subsetItems.length > 0),
        ],
      };
    }
    this.sortAllItems();
    return order;
  }

  sortAllItems() {
    if (this.allItems && this.allItems.length > 1) {
      const indexOfMovingItem = this.allItems.findIndex((item) => +item.id === +this.itemId);
      if (indexOfMovingItem === 0) {
        return;
      }
      const itemToMove = this.allItems[0];
      this.allItems[0] = this.allItems[indexOfMovingItem];
      this.allItems[indexOfMovingItem] = itemToMove;
    }
  }

  expanded(bookIndex) {
    return !!this.expand[bookIndex];
  }

  toggleMore(bookIndex) {
    this.expand[bookIndex] = !this.expand[bookIndex];
    this.detectChanges();
    setTimeout(() => {
      this.calculateTopPosition();
    }, 400);
  }

  setAllBackToStock() {
    this.collect({}, false, OrderStatus.BackToStock);
  }

  setAllArrived() {
    this.collect({}, false, OrderStatus.Arrived);
    this.printLabelIfNeeded();
  }

  printLabelIfNeeded() {
    if (this.labelChecked && this.allItems && this.allItems.length > 1) {
      const subsetIds = this.allItems.map((data) => data.subsetItems[0].id);
      this.printerService.printGoodsInLabel(subsetIds);
    }
  }

  collect(collection: { [key: string]: number } = {}, partial = false, newStatus) {
    if (this.order && this.order.items) {
      const orderId = this.order.id;
      const items = this.order.items as OrderItemDTO[];

      const patchingSubsetItemSequentially$: Observable<{
        orderId: number;
        orderItemId: number;
        orderItemSubsetId: number;
        quantity: number;
        processingStatus: number;
      }> = new Observable((observer) => {
        this.order.items.forEach((orderItem) => {
          const orderItemId = orderItem.id;
          if (orderItem.data.subsetItems && orderItem.data.subsetItems[0]) {
            const orderItemSubsetId = orderItem.data.subsetItems[0].id;
            const quantity = partial
              ? collection && collection[orderItem.data.orderItemNumber]
                ? collection[orderItem.data.orderItemNumber]
                : 0
              : orderItem && orderItem.data.quantity
              ? orderItem.data.quantity
              : 0;
            if (quantity > 0 && orderId && orderItemId && orderItemSubsetId) {
              observer.next({
                orderId: orderId,
                orderItemId: orderItemId,
                orderItemSubsetId: orderItemSubsetId,
                quantity: quantity,
                processingStatus: newStatus,
              });
            }
          }
        });
      });
      this.listenStatusChanges(patchingSubsetItemSequentially$, partial, items.length, collection, newStatus);
    }
  }

  listenStatusChanges(
    patchingSubsetItemSequentially$: Observable<{
      orderId: number;
      orderItemId: number;
      orderItemSubsetId: number;
      quantity: number;
      processingStatus: number;
    }>,
    partial: boolean,
    itemLength: number,
    collection: { [key: string]: number },
    newStatus: number
  ) {
    let itemsProcessed = 0;
    let tag: string;
    tagsValues.forEach((key) => {
      const currTag = this.tags[+key];
      if (currTag.selected) {
        tag = currTag.label;
      }
    });
    patchingSubsetItemSequentially$
      .pipe(
        takeUntil(this.destroy$),
        concatMap((data) => {
          return this.shelfService.changeOrderArticleStatus(
            data.orderId,
            data.orderItemId,
            data.orderItemSubsetId,
            data.quantity,
            data.processingStatus,
            tag
          );
        }),
        catchError((error) => {
          console.error(error);
          return of(undefined);
        })
      )
      .subscribe((response: { itemId: number; result: OrderItemSubsetDTO }) => {
        itemsProcessed++;
        if (response && itemsProcessed === itemLength) {
          this.store.dispatch(new SetBranchProcessCurrentPath('/goodsin/results', true));
          this.router.navigate(['/goodsin/results']);
        }
      });
  }

  updateEstimatedShippingDate(date: Date) {
    const orderId = this.order.id;
    const items = this.order.items;
    const extendingEstimatedShippingDateSequentially$: Observable<{
      orderId: number;
      orderItemId: number;
      orderItemSubsetId: number;
      date: Date;
    }> = new Observable((observer) => {
      items.forEach((orderItem) => {
        const orderItemId = orderItem.id;
        if (orderItem.data && orderItem.data.subsetItems && orderItem.data.subsetItems[0]) {
          const orderItemSubsetId = orderItem.data.subsetItems[0].id;
          const observation = {
            orderId: orderId,
            orderItemId: orderItemId,
            orderItemSubsetId: orderItemSubsetId,
            date: date,
          };
          observer.next(observation);
        }
      });
    });

    this.listenEstimationShippingDateUpdates(extendingEstimatedShippingDateSequentially$, items.length);
  }

  listenEstimationShippingDateUpdates(
    extendingEstimatedShippingDateSequentially$: Observable<{
      orderId: number;
      orderItemId: number;
      orderItemSubsetId: number;
      date: Date;
    }>,
    itemLength: number
  ) {
    let itemsProcessed = 0;
    extendingEstimatedShippingDateSequentially$
      .pipe(
        takeUntil(this.destroy$),
        concatMap((data) => {
          return this.shelfService.updateEstimatedShippingDate(data.orderId, data.orderItemId, data.orderItemSubsetId, data.date).pipe(
            map((result) => {
              return { date: data.date, result: result };
            })
          );
        }),
        catchError((error) => {
          return of(undefined);
        })
      )
      .subscribe((response) => {
        itemsProcessed++;
        if (itemsProcessed === itemLength && response) {
          this.esitmatedShippingDate = response.date;
        }
      });
  }

  editOrder() {
    if (this.order) {
      this.store.dispatch(new SetEditOrder(this.order));
      let breadcrumbName = '';
      if (this.order.buyer) {
        breadcrumbName = this.order.buyer.firstName + ' ' + this.order.buyer.lastName + ' Bearbeiten';
      }
      // tslint:disable-next-line: max-line-length
      const path = `/shelf/edit/${this.order.id}/${this.order.id}/${this.processingStatus}/o/goodsin/${this.order.buyer.source}/${this.order.items[0].id}`;
      this.store.dispatch(
        new AddBreadcrumb(
          <Breadcrumb>{
            name: breadcrumbName,
            path: path,
          },
          'goodsin'
        )
      );
      this.store.dispatch(new SetBranchProcessCurrentPath(path));
      this.router.navigate([path]);
    }
  }

  tagUpdated(value: { label: string; selected: boolean }, tag: Tag) {
    tagsValues.forEach((key) => {
      if (this.tags[key]) {
        this.tags[key].selected = false;
      }
    });
    this.tags[tag] = value;
  }

  labelChanged(selected: boolean) {
    this.labelChecked = selected;
  }

  changeInputStatus(input: string, status: boolean) {
    this.notAvailableModel[input].disabled = status;
  }

  clear(input: string) {
    this.notAvailableModel[input].value = '';
  }

  addComment(orderItem: OrderItemDTO) {
    orderItem.subsetItems.forEach((subsetItem) => {
      this.shelfService
        .patchOrderItemSubset(this.orderId, orderItem.id, subsetItem.id, {
          ...subsetItem,
          specialComment: this.notAvailableModel.comment.value,
        })
        .pipe(
          takeUntil(this.destroy$),
          catchError((error) => {
            console.error('Error Cought', error);
            return of(undefined);
          })
        )
        .subscribe((response: OrderItemSubsetDTO) => {
          if (response) {
            this.notAvailableModel.comment.disabled = true;
          }
        });
    });
  }

  // functions for navigational arrow of all items
  scrollBack() {
    const moveCalc = this.allItemsEL.nativeElement.scrollLeft - this.offset;
    this.allItemsEL.nativeElement.scrollTo({ left: moveCalc, behavior: 'smooth' });

    this.showBackContainer = this.allItemsEL.nativeElement.scrollLeft - this.offset > 0;
    this.showBack = false;

    this.showFowardContainer = this.allItemsEL.nativeElement.scrollLeft < this.allItemsEL.nativeElement.scrollWidth;
    this.showNext = false;
  }

  scrollFoward() {
    const moveCalc = this.allItemsEL.nativeElement.scrollLeft + this.offset;
    this.allItemsEL.nativeElement.scrollTo({ left: moveCalc, behavior: 'smooth' });

    this.showFowardContainer = moveCalc + this.offset * 2 < this.allItemsEL.nativeElement.scrollWidth;
    this.showNext = false;

    // If clicked next, then back button is always there
    this.showBackContainer = true;
  }

  showFowardIcon() {
    this.calculateTopPosition();
    this.showNext = true;
  }

  hideFowardIcon() {
    this.showNext = false;
  }

  showBackIcon() {
    this.calculateTopPosition();
    this.showBack = true;
  }

  hideBackIcon() {
    this.showBack = false;
  }

  public calculateTopPosition() {
    this.detectChanges();
    const detailsElement = document.getElementById('card-container');
    if (detailsElement && this.allItemsEL) {
      const pos = this.allItemsEL.nativeElement.offsetTop - detailsElement.scrollTop;
      this.topPosition = pos;
    }
  }

  initShowArrows() {
    this.detectChanges();
    if (this.allItemsEL) {
      this.offset = Math.ceil(this.allItemsEL.nativeElement.clientWidth / 2);

      const lastformat = this.allItemsEL.nativeElement.children[this.allItemsEL.nativeElement.children.length - 2];

      if (lastformat) {
        const lastPos = lastformat.offsetLeft + lastformat.offsetWidth;
        this.showFowardContainer = lastPos > this.allItemsEL.nativeElement.clientWidth;
        this.detectChanges();
      }
    }
  }

  @HostListener('window:scroll', ['$event'])
  scrollHandler(event) {
    if (event.target.id === 'card-container') {
      if (event.target.scrollTop === 0) {
        this.calculateTopPosition();
      }
      if (event.target.scrollTop === event.target.scrollHeight - event.target.offsetHeight) {
        this.calculateTopPosition();
      }
    } else if (event.target.id === 'all-items' && !this.isIPad) {
      if (event.target.scrollLeft > 0) {
        this.showBackContainer = true;
      }
      this.initShowArrows();
    }
  }
  // --end--

  detectChanges() {
    setTimeout(() => {
      if (this.cdr !== null && this.cdr !== undefined && !(this.cdr as ViewRef).destroyed) {
        this.cdr.detectChanges();
      }
    }, 0);
  }
}
