import { Observable, Subject } from 'rxjs';
import { map, takeUntil, filter, tap, startWith, pairwise, distinctUntilChanged } from 'rxjs/operators';
import { Component, OnDestroy, OnInit, ViewChild, ChangeDetectorRef } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { Router } from '@angular/router';
import { Select, Store } from '@ngxs/store';
import { BookData } from '../../../../core/models/book-data.model';
import { Breadcrumb } from '../../../../core/models/breadcrumb.model';
import { Cart } from '../../../../core/models/cart.model';
import { DeliveryOption } from '../../../../core/models/delivery-option.model';
import { ProcessCart } from '../../../../core/models/process-cart.model';
import { AddBreadcrumb } from '../../../../core/store/actions/breadcrumb.actions';
import { ChangeCurrentRoute } from '../../../../core/store/actions/process.actions';
import { SharedSelectors } from '../../../../core/store/selectors/shared.selectors';
import { PayMethodComponent } from '../../components/pay-method/pay-method.component';
import { PrinterService } from '../../../../core/services/printer.service';
import { PrinterSelectionComponent } from '../../../../components/printer-selection/printer-selection.component';
import { User } from '../../../../core/models/user.model';
import { isArrayMinLength } from '../../../../core/utils/app.utils';
import { removeItemCartAnimation } from '../../../../shared/animations/remove.animation';

import { ButtonComponent } from '@libs/ui';
import { cartPaymentType } from 'apps/sales/src/app/shared/animations/cart-paymet-type.animation';
import { ErrorService } from 'apps/sales/src/app/core/error/component/error.service';
import { CustomerFeatures } from 'apps/sales/src/app/core/models/customer-features.model';
import { CartService } from '../services/cart.service';
import { BranchInfoDTO } from '@swagger/isa';
import { AppState } from 'apps/sales/src/app/core/store/state/app.state';

export interface CartReviewItem extends BookData {
  cartEntryId: number;
  branch: BranchInfoDTO;
  cartId: number;
}

export const deliveryFilter = (deliveryOption: string) => (items: Cart[]) => items.filter((b) => b.deliveryType === deliveryOption);
export const cartToCartReviewArray = (cart: Cart[]) => {
  return cart
    .filter((d) => !!d)
    .map((bookInCart: Cart) => {
      if (bookInCart) {
        const currency = bookInCart.book.catalogAvailability.price.value.currency;
        const price = bookInCart.book.catalogAvailability.price.value.value;

        return {
          id: bookInCart.cartId,
          book: bookInCart.book,
          quantity: bookInCart.quantity,
          currency: currency,
          price: price,
          imgUrl: bookInCart.book.product.ean,
          deliveryType: bookInCart.deliveryType,
          cartEntryId: bookInCart.cartEntryId,
          branch: bookInCart.branch,
          cartId: bookInCart.cartId,
          orderTypeChanged: bookInCart.orderTypeChanged,
          avaMessage: bookInCart.avaMessage,
          pickUpPrice: bookInCart.pickUpPrice,
          deliveryPrice: bookInCart.deliveryPrice,
          deliveryDate: bookInCart.deliveryDate,
          downloadPrice: bookInCart.downloadPrice,
          pickUpDate: bookInCart.pickUpDate,
        } as CartReviewItem;
      }
    });
};

@Component({
  selector: 'app-cart-new',
  templateUrl: './cart-review.component.html',
  styleUrls: ['./cart-review.component.scss'],
  animations: [removeItemCartAnimation, cartPaymentType],
})
export class CartReviewComponent implements OnInit, OnDestroy {
  @Select(SharedSelectors.getCart) cartData$: Observable<ProcessCart>;
  @ViewChild('payMethod') payMethodDialog: PayMethodComponent;
  @ViewChild('printModal') printModal: PrinterSelectionComponent;
  @ViewChild('continue') continueBtn: ButtonComponent;

  deliveryCount$: Observable<number>;
  takeNowCount$: Observable<number>;
  pickUpCount$: Observable<number>;
  downloadCount$: Observable<number>;
  cartReady$: Observable<boolean>;
  loaded = false;
  lastTakeNowLength = -1;
  takeNowLength = -1;
  lastDeliveryLength = -1;
  deliveryLength = -1;
  deliveryAnimation = '';
  pickUpAnimation = '';
  orderDisabled = false;
  customerFeatures: CustomerFeatures;
  disableAddressChanging = false;

  cartId: number;

  paymentOption = {
    delivery: false,
    pickUp: false,
    takeNow: false,
    download: false,
  };

  destroy$ = new Subject();
  deliveryAddress = '';
  notificationForm: FormGroup;

  total = {
    price: 0,
    items: 0,
  };

  customer: User;

  showNotificationSettings = true;

  constructor(
    private store: Store,
    private router: Router,
    private printer: PrinterService,
    private errorService: ErrorService,
    private cartService: CartService
  ) {
    this.deliveryCount$ = this.cartData$.pipe(
      startWith(-1),
      filter((c) => isArrayMinLength(c, 'cart', 0) || c === -1),
      map((c) => {
        if (c === -1) {
          return c;
        } else {
          const data = c as ProcessCart;
          return data.cart;
        }
      }),
      map((c) => {
        if (c === -1) {
          return c;
        } else {
          const data = c as Cart[];
          return deliveryFilter(DeliveryOption.DELIVERY)(data);
        }
      }),
      map((i) => {
        if (i === -1) {
          return i;
        } else {
          const data = i as Cart[];
          return data.length;
        }
      }),
      pairwise(),
      map((value) => {
        if (value[0] !== -1) {
          this.lastDeliveryLength = value[0];
        } else {
          this.lastDeliveryLength = value[1];
        }
        this.deliveryLength = value[1];
        return value[1];
      })
    );
    this.takeNowCount$ = this.cartData$.pipe(
      startWith(-1),
      filter((c) => isArrayMinLength(c, 'cart', 0) || c === -1),
      map((c) => {
        if (c === -1) {
          return c;
        } else {
          const data = c as ProcessCart;
          return data.cart;
        }
      }),
      map((c) => {
        if (c === -1) {
          return c;
        } else {
          const data = c as Cart[];
          return deliveryFilter(DeliveryOption.TAKE_NOW)(data);
        }
      }),
      map((i) => {
        if (i === -1) {
          return i;
        } else {
          const data = i as Cart[];
          return data.length;
        }
      }),
      pairwise(),
      map((value) => {
        if (value[0] !== -1) {
          this.lastTakeNowLength = value[0];
        } else {
          this.lastTakeNowLength = value[1];
        }
        this.takeNowLength = value[1];
        return value[1];
      })
    );
    this.pickUpCount$ = this.cartData$.pipe(
      filter((c) => isArrayMinLength(c, 'cart', 0)),
      map((c) => c.cart),
      map(deliveryFilter(DeliveryOption.PICK_UP)),
      map((i) => i.length)
    );
    this.downloadCount$ = this.cartData$.pipe(
      filter((c) => isArrayMinLength(c, 'cart', 0)),
      map((c) => c.cart),
      map(deliveryFilter(DeliveryOption.DOWNLOAD)),
      map((i) => i.length)
    );
  }

  ngOnInit() {
    this.cartData$
      .pipe(
        filter((x) => !!x),
        takeUntil(this.destroy$)
      )
      .subscribe((data) => {
        if (data.cart.length > 0) {
          this.loaded = true;
          this.customer = data.customer;
          this.disableAddressChanging = false;
          if (data.customer && data.customer.features) {
            this.customerFeatures = new CustomerFeatures(data.customer.features);
            this.customerFeaturesSpecificBehaviourHandling(this.customerFeatures);
          }
          this.recalculate(data.cart);
          this.checkDeliveryOptions(data.cart);
        } else {
          this.loaded = false;
        }
      });

    this.errorService
      .recovery$()
      .pipe(takeUntil(this.destroy$))
      .subscribe((status) => {
        if (this.continueBtn) {
          this.continueBtn.stopLoading();
        }
      });

    this.cartService.orderError$.pipe(takeUntil(this.destroy$)).subscribe(() => {
      if (this.continueBtn) {
        this.continueBtn.stopLoading();
      }
    });

    this.store
      .select(AppState.getCurrentProcessId)
      .pipe(takeUntil(this.destroy$))
      .subscribe(() => {
        this.reloadNotifications();
      });
  }

  private reloadNotifications() {
    this.showNotificationSettings = false;
    setTimeout(() => {
      this.showNotificationSettings = true;
    }, 40);
  }

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

  customerFeaturesSpecificBehaviourHandling(features: CustomerFeatures) {
    if (!features) {
      return;
    }
    features.employee.ifTrue(() => (this.disableAddressChanging = true));
  }

  getDeliveryDeleteAnimation() {
    const animation = this.takeNowLength < this.lastTakeNowLength ? 'delete' : '';
    const status = this.takeNowLength === 0 ? 'stop' : animation;
    return status;
  }

  getPickUpDeleteAnimation() {
    const changingTakeNow = this.takeNowLength !== this.lastTakeNowLength;
    const changingDelivery = this.deliveryLength !== this.lastDeliveryLength;
    const animation = this.takeNowLength < this.lastTakeNowLength || this.deliveryLength < this.lastDeliveryLength ? 'delete' : '';
    const status = changingTakeNow
      ? this.takeNowLength === 0
        ? 'stop'
        : animation
      : changingDelivery
      ? this.takeNowLength === 0
        ? 'stop'
        : animation
      : 'stop';
    return status;
  }

  private checkDeliveryOptions(books) {
    this.paymentOption.delivery = books.some((book) => book.deliveryType === DeliveryOption.DELIVERY);
    this.paymentOption.pickUp = books.some((book) => book.deliveryType === DeliveryOption.PICK_UP);
    this.paymentOption.takeNow = books.some((book) => book.deliveryType === DeliveryOption.TAKE_NOW);
    this.paymentOption.download = books.some((book) => book.deliveryType === DeliveryOption.DOWNLOAD);
  }

  recalculate(cart: Cart[]) {
    let totalSum = 0;
    let totalQuantity = 0;

    cart.forEach((book) => {
      let price = book.book.catalogAvailability.price.value.value;
      if (book.deliveryType === DeliveryOption.PICK_UP && book.pickUpPrice) {
        price = book.pickUpPrice;
      }

      if (book.deliveryType === DeliveryOption.DELIVERY) {
        price = book.deliveryPrice;
      }

      if (book.deliveryType === DeliveryOption.DOWNLOAD) {
        price = book.downloadPrice;
      }

      totalSum += price * book.quantity;
      totalQuantity += book.quantity;
    });

    this.total.items = totalQuantity;
    this.total.price = totalSum;
  }

  next() {
    if (this.customer) {
      // Crete order
      this.continueBtn.startLoading();
      this.payMethodDialog.directOrder();
    } else {
      // No customer redirect to search
      let newBread: Breadcrumb;
      newBread = {
        name: 'Kundensuche',
        path: 'customer/search',
      };
      this.navigate(newBread, 'customer');
    }
  }

  search() {
    this.store.dispatch(
      new AddBreadcrumb(
        <Breadcrumb>{
          name: 'Artikelsuche',
          path: '/product/search',
        },
        'product',
        true
      )
    );
    this.store.dispatch(new ChangeCurrentRoute('/product/search'));
    this.router.navigate(['/product/search']);
  }

  async print(printer: string) {
    const cartId = this.store.selectSnapshot(SharedSelectors.getCurrentCartId);
    if (cartId) {
      const printResponse = await this.printer.printCart(cartId, printer);
      if (!printResponse.error) {
        this.printModal.closeModal();
      } else {
        this.printModal.setError(printResponse.message);
      }
    }
  }

  selectPrinter() {
    this.printModal.openDialog();
  }

  private navigate(bread: Breadcrumb, menu: string) {
    this.store.dispatch(new ChangeCurrentRoute(bread.path));
    this.store.dispatch(new AddBreadcrumb(bread, menu));
    this.router.navigate([bread.path]);
  }

  enableDisableOrder(disable: boolean) {
    this.orderDisabled = disable;
  }
}
