import { Component, OnInit, Output, EventEmitter, Input, OnDestroy, ViewChild, ChangeDetectorRef, ViewRef } from '@angular/core';
import { ModalService, DeleteDropdownComponent } from '@libs/ui';
import { Router } from '@angular/router';
import { Store, Select } from '@ngxs/store';
import { ItemDTO } from '@swagger/cat';
import { DeliveryOption } from '../../../../core/models/delivery-option.model';
import { ChangeCurrentRoute } from '../../../../core/store/actions/process.actions';
import { SetCartEntry } from '../../../../core/store/actions/cart-entry.actions';
import { Subject, Observable, of } from 'rxjs';
import { tap, map, filter, take } from 'rxjs/operators';
import { AddBreadcrumb } from '../../../../core/store/actions/breadcrumb.actions';
import { Breadcrumb } from '../../../../core/models/breadcrumb.model';
import { BranchSelectors } from '../../../../core/store/selectors/branch.selector';
import { ProductAvailability } from '../../../../core/models/product-availability.model';
import { CheckoutType } from '../../../../core/models/checkout-type.enum';
import { BranchDTO } from '@swagger/checkout';
import { AvailabilityDTO } from '@swagger/availability';
import { mapToIterable, distance, objectNotNull } from '../../../../core/utils/app.utils';
import { isNullOrUndefined } from 'util';
import { ProductAvailabilityService } from '../../../../core/services/product-availability.service';
import { BranchService } from '../../../../core/services/branch.service';
import { DatePipe } from '@angular/common';
import { ProcessCart } from '../../../../core/models/process-cart.model';
import { SharedSelectors } from '../../../../core/store/selectors/shared.selectors';
import { SearchDropdownComponent } from '@libs/ui/lib/search-dropdown';
import { AddBranchesIfNotLoaded } from 'apps/sales/src/app/core/store/actions/branch.actions';
import { allowedAvailabilityStatusCodes } from 'apps/sales/src/app/core/utils/product.util';

@Component({
  selector: 'app-checkout',
  templateUrl: './product-checkout.component.html',
  styleUrls: ['./product-checkout.component.scss'],
})
export class ProductCheckoutComponent implements OnInit, OnDestroy {
  @Select(SharedSelectors.getCart) cartData$: Observable<ProcessCart>;
  @Select(SharedSelectors.getBreadcrumbs) getBreadcrumbs$: Observable<Breadcrumb[]>;
  @Select(BranchSelectors.getBranches) branches$: Observable<{
    [key: number]: BranchDTO;
  }>;

  @ViewChild('deleteDropdown')
  deleteDropdown: DeleteDropdownComponent;
  @ViewChild('branchesdropdown')
  branchesdd: SearchDropdownComponent;

  id = 'checkout-modal';
  stepOne = true;
  stepTwoImgType = '';
  deliveryType = '';
  deliveryOptions = {
    delivery: DeliveryOption.DELIVERY,
    now: DeliveryOption.TAKE_NOW,
    pick_up: DeliveryOption.PICK_UP,
  };
  branches: string[] = [];
  filteredBranches: string[] = [];
  branchesData: BranchDTO[] = [];
  branchList: BranchDTO[] = [];
  breadcrumbs: Breadcrumb[] = [];

  currentLocation = '';
  @Input() currentPickUpDate: string;
  @Input() currentDeliveryDate: string;
  @Input() isDownload = false;
  selectedBranch: BranchDTO;

  currentNumberOfItems = 1;
  possibleItems = [1, 2, 3, 4, 5, 6, 7, 8, 9];
  displayItemsNumber = 1;
  currentPrice = '';
  currentPoints = '';
  currency = '';
  destroy$ = new Subject();
  customerAlredySet = false;
  currentAvailability: AvailabilityDTO;
  quantityError;
  branchLoad = false;
  searchingNewBranch = false;
  addItemsToCartDisabled = true;
  loading = true;
  _pikUpPrice: number;
  _deliveryPrice: number;
  _downloadPrice: number;

  confirmationBtnText = 'Fortfahren';

  // Trigger other functionality if needed
  @Output() closed: EventEmitter<boolean> = new EventEmitter();

  @Input() availability: ProductAvailability[];

  get takeNowAvailable() {
    if (this.availability.filter((t) => t.type === CheckoutType.takeNow).length > 0) {
      return of(true);
    }
    return of(false);
  }

  get storeAvailable() {
    if (
      this.availability.filter(
        (t) =>
          t.type === CheckoutType.store &&
          (allowedAvailabilityStatusCodes(t.status) || (t.av && t.av.ssc === '830' && t.av.supplier === 'G'))
      ).length > 0
    ) {
      return of(true);
    }
    return of(false);
  }

  get shippingAvailable() {
    if (this.availability.filter((t) => t.type === CheckoutType.delivery && allowedAvailabilityStatusCodes(t.status)).length > 0) {
      return of(true);
    }
    return of(false);
  }

  private _book: ItemDTO;
  @Input() set book(val: ItemDTO) {
    if (val) {
      this._book = val;
    }
  }

  get book() {
    return this._book;
  }

  get price(): number {
    if (this._book.catalogAvailability.price) {
      return this._book.catalogAvailability.price.value.value;
    }
  }

  @Input() set avpickupprice(val: number) {
    if (val) {
      this._pikUpPrice = val;
    }
  }

  @Input() set avdeliveryprice(val: number) {
    if (val) {
      this._deliveryPrice = val;
    }
  }

  get pickUpPrice(): number {
    if (this._pikUpPrice) {
      return this._pikUpPrice;
    }
    if (this._book.catalogAvailability.price) {
      return this._book.catalogAvailability.price.value.value;
    }
  }

  @Input() set avdownloadprice(val: number) {
    if (val) {
      this._downloadPrice = val;
    }
  }

  get deliverPrice(): number {
    if (this._deliveryPrice) {
      return this._deliveryPrice;
    }
    if (this._book.catalogAvailability.price) {
      return this._book.catalogAvailability.price.value.value;
    }
  }

  get downloadPrice(): number {
    if (this._downloadPrice) {
      return this._downloadPrice;
    }
    if (this._book.catalogAvailability.price) {
      return this._book.catalogAvailability.price.value.value;
    }
  }

  get selectedPrice(): number {
    switch (this.deliveryType) {
      case DeliveryOption.PICK_UP:
        return this.pickUpPrice;
      case DeliveryOption.DELIVERY:
        return this.deliverPrice;
      case DeliveryOption.DOWNLOAD:
        return this.downloadPrice;
      default:
        return this.price;
    }
  }

  get preferredDownloadAvailability(): AvailabilityDTO {
    if (this.availability) {
      const downloadAvailabilites = this.availability.filter((availability) => availability.type === CheckoutType.donwload);

      if (downloadAvailabilites) {
        const preferredProductAvailability = downloadAvailabilites.find(
          (productAvailability) => !!productAvailability.av && !!productAvailability.av.preferred
        );

        if (preferredProductAvailability) {
          return preferredProductAvailability.av;
        }
      }
    }
  }

  constructor(
    private modalService: ModalService,
    private store: Store,
    private router: Router,
    private productAvailabilityService: ProductAvailabilityService,
    private branchService: BranchService,
    private datePipe: DatePipe,
    private cdrf: ChangeDetectorRef
  ) {}

  ngOnInit() {
    if (this.isDownload) {
      this.stepTwoImgType = 'download-type';
      this.possibleItems = [1];
      this.deliveryType = DeliveryOption.DOWNLOAD;
      if (this.preferredDownloadAvailability) {
        this.currentAvailability = this.preferredDownloadAvailability;
      }
    }

    this.currency = this.book.catalogAvailability.price ? this.book.catalogAvailability.price.value.currency : null;
    this.currentPoints = this.book.promoPoints + '';

    this.branches$
      .pipe(
        filter((branchData) => objectNotNull(branchData)),
        take(1)
      )
      .subscribe((branchData: { [key: number]: BranchDTO }) => {
        this.processBranchData(branchData);
      });

    this.cartData$.pipe(take(1)).subscribe((data: ProcessCart) => {
      if (data && data.customer) {
        this.customerAlredySet = true;
      }
    });

    this.getBreadcrumbs$.pipe(take(1)).subscribe((data: Breadcrumb[]) => {
      if (data) {
        this.breadcrumbs = data;
      }
    });
  }

  private processBranchData(branchData: { [key: number]: BranchDTO }) {
    const fullBranches: BranchDTO[] = mapToIterable(branchData);
    const branches = fullBranches.filter((branch: BranchDTO) => {
      if (
        branch &&
        !isNullOrUndefined(branch.isOnline) &&
        branch.isOnline &&
        !isNullOrUndefined(branch.isShippingEnabled) &&
        branch.isShippingEnabled &&
        !isNullOrUndefined(branch.isOrderingEnabled) &&
        branch.isOrderingEnabled
      ) {
        return true;
      } else {
        return false;
      }
    });

    this.branchList = branches;
    const userBranchNumber = this.store.selectSnapshot(BranchSelectors.getUserBranch);
    const userBranch = branches.find((t) => t.branchNumber === userBranchNumber);
    if (!!userBranch) {
      this.selectedBranch = userBranch;
      this.currentLocation = userBranch.name;
    } else {
      this.selectedBranch = branches[0];
      this.currentLocation = branches[0].name;
    }
    // Dropdown options
    this.branches.push(this.currentLocation);
    this.branchesData.push(this.selectedBranch);
    branches.forEach((_branches: BranchDTO) => {
      if (_branches.id === this.selectedBranch.id) {
        return;
      }
      this.branches.push(_branches.name);
      this.branchesData.push(_branches);
    });
    this.filteredBranches = this.branches;
    if (!!userBranch) {
      this.filteredBranches = this.branchesData
        .sort((a: BranchDTO, b: BranchDTO) => this.branchSorterFn(a, b, userBranch))
        .map((t) => t.name);
    }
  }

  private branchSorterFn(a: BranchDTO, b: BranchDTO, userBranch: BranchDTO) {
    const userLat = userBranch.address && userBranch.address.geoLocation ? userBranch.address.geoLocation.latitude : 0;
    const userLong = userBranch.address && userBranch.address.geoLocation ? userBranch.address.geoLocation.longitude : 0;
    const aLat = a.address && a.address.geoLocation ? a.address.geoLocation.latitude : 0;
    const aLong = a.address && a.address.geoLocation ? a.address.geoLocation.longitude : 0;
    const bLat = b.address && b.address.geoLocation ? b.address.geoLocation.latitude : 0;
    const bLong = b.address && b.address.geoLocation ? b.address.geoLocation.longitude : 0;
    const aDist = distance(userLat, userLong, aLat, aLong);
    const bDist = distance(userLat, userLong, bLat, bLong);
    return aDist - bDist;
  }

  branchSearch(term: string) {
    const userBranchNumber = this.store.selectSnapshot(BranchSelectors.getUserBranch);
    const branchList = [...this.branchesData];
    const userBranch = branchList.find((t) => t.branchNumber === userBranchNumber);
    if (isNullOrUndefined(term) || term.length < 1) {
      this.filteredBranches = branchList.sort((a: BranchDTO, b: BranchDTO) => this.branchSorterFn(a, b, userBranch)).map((t) => t.name);
      return;
    }
    this.branchService
      .searchBranches(term, 0, null)
      .pipe(
        take(1),
        filter((branches) => {
          if (branches && branches.length > 0) {
            return true;
          } else {
            return false;
          }
        }),
        map((branches) => {
          return branches.filter((branch) => {
            if (
              branch &&
              !isNullOrUndefined(branch.isOnline) &&
              branch.isOnline &&
              !isNullOrUndefined(branch.isShippingEnabled) &&
              branch.isShippingEnabled &&
              !isNullOrUndefined(branch.isOrderingEnabled) &&
              branch.isOrderingEnabled
            ) {
              return true;
            } else {
              return false;
            }
          });
        }),
        map((branches) => {
          this.store.dispatch(new AddBranchesIfNotLoaded(branches));
          if (branches) {
            branches.forEach((br) => {
              const existsInBranchData = this.branchesData.find((t) => t.branchNumber === br.branchNumber);
              if (!existsInBranchData) {
                this.branches.push(br.name);
                this.branchesData.push(br);
              }
            });
          }
          return branches.map((t) => t.name);
        }),
        tap((branchName) => {
          this.filteredBranches = branchName;
        })
      )
      .subscribe();
  }

  storeBranches(): Observable<number[]> {
    return of(this.availability.filter((t) => t.type === CheckoutType.store).map((t) => t.branchId));
  }

  // STEP ONE
  selectLocation(location: string) {
    this.branchLoad = true;
    this.searchingNewBranch = true;
    const branch = this.branchesData.find((b: BranchDTO) => b.name === location);
    // reset store availability
    this.availability = [...this.availability.filter((t) => t.type !== CheckoutType.store)];
    this.productAvailabilityService
      .getStoreAvailability(this.book, this.book.product.ean, branch.id)
      .pipe(
        tap((response) => {
          if (response.av && response.av.length > 0) {
            this.currentLocation = location;
            this.filteredBranches = [location, ...this.branches.filter((t) => t !== location)];
            this.selectedBranch = branch;
            const preferedAv = response.av.find((t) => t.preferred === 1);
            this.currentPickUpDate = this.datePipe.transform(new Date(preferedAv.at), 'dd.MM.yy');
            response.av.forEach((t) => {
              this.availability.push({
                itemId: t.itemId,
                branchId: branch.id,
                quantity: response.av.length,
                type: CheckoutType.store,
                av: preferedAv ? preferedAv : response.av[0],
                status: t.status,
              });
            });
          }
          this.branchesdd.stopLoading();
          this.branchLoad = false;
          this.searchingNewBranch = false;
        })
      )
      .subscribe();
  }

  selectedAction(action: string) {
    if (this.branchLoad) {
      return;
    }
    if (action === DeliveryOption.TAKE_NOW) {
      this.currentAvailability = this.availability.find((t) => t.type === CheckoutType.takeNow).av;
      this.stepTwoImgType = 'Take_now';
      this.deliveryType = DeliveryOption.TAKE_NOW;
      this.confirmationBtnText = 'Reservieren';
      // Set user branch as selected branch
      const userBranchNumber = this.store.selectSnapshot(BranchSelectors.getUserBranch);
      const userBranch = this.branchList.find((t) => t.branchNumber === userBranchNumber);
      if (!!userBranch) {
        this.selectedBranch = userBranch;
        this.currentLocation = userBranch.name;
      }
    } else if (action === DeliveryOption.PICK_UP) {
      if (!this.availability.find((t) => t.type === CheckoutType.store)) {
        return;
      }
      this.currentAvailability = this.availability.find((t) => t.type === CheckoutType.store).av;
      this.stepTwoImgType = 'Package_Icon';
      this.deliveryType = DeliveryOption.PICK_UP;
      this.confirmationBtnText = 'Fortfahren';
    } else {
      this.currentAvailability = this.availability.find((t) => t.type === CheckoutType.delivery).av;
      this.stepTwoImgType = 'truck_Icon';
      this.deliveryType = DeliveryOption.DELIVERY;
      this.confirmationBtnText = 'Fortfahren';
    }
    this.switchSteps();
    this.cdrf.detectChanges();
    if (this.deleteDropdown) {
      this.deleteDropdown.startLoading();
    }
    this.setNumberOfItems(this.currentNumberOfItems);
  }

  // STEP TWO
  async setNumberOfItems(numberOfItems: number) {
    this.currentNumberOfItems = numberOfItems;
    this.displayItemsNumber = numberOfItems;
    this.currentPoints = numberOfItems * this.book.promoPoints + '';

    if (numberOfItems > 999) {
      this.setQuantityError(numberOfItems, 999);
      this.addItemsToCartDisabled = true;
    } else {
      this.addItemsToCartDisabled = true;
      this.loading = true;
      const av = await this.validateAvailability(numberOfItems);
      this.setQuantityError(numberOfItems, av);
      const isTakeNowAndSelectedMoreThanInStock = this.deliveryType === DeliveryOption.TAKE_NOW && numberOfItems > av;
      isTakeNowAndSelectedMoreThanInStock.ifFalse(() => {
        this.addItemsToCartDisabled = false;
      });
      this.loading = false;
    }

    if (this.deleteDropdown) {
      this.deleteDropdown.stopLoading();
    }
  }

  setQuantityError(numberOfItems: number, limit: number) {
    if (numberOfItems > 999) {
      this.quantityError = `Achtung, Maximal ${limit} Exemplare bestellbar.`;
    } else if (numberOfItems > limit) {
      this.quantityError = `${limit} ${limit > 1 ? 'Exemplare' : 'Exemplar'} sofort lieferbar.`;
    } else {
      this.quantityError = null;
    }
  }

  async validateAvailability(numberOfItems: number): Promise<number> {
    if (this.deliveryType === DeliveryOption.TAKE_NOW) {
      return of(
        this.availability
          .filter((t) => t.type === CheckoutType.takeNow)
          .map((response) => {
            return response.quantity;
          })
          .reduce((q1, q2) => {
            return q1 + q2;
          })
      ).toPromise();
    } else if (this.deliveryType === DeliveryOption.PICK_UP) {
      const storeAvailability = await this.productAvailabilityService
        .getStoreAvailability(this.book, this.book.product.ean, this.selectedBranch.id, numberOfItems)
        .pipe(
          map((response) => {
            if (response && response.av) {
              const preferredSupplier = response.av.find((av) => av && av.preferred === 1);
              if (preferredSupplier) {
                this.currentAvailability = preferredSupplier;
                this._pikUpPrice = preferredSupplier.price.value.value;
              }

              const eligibleAvailabilities = response.av
                .filter((availability) => allowedAvailabilityStatusCodes(availability.status) && availability.qty)
                .map((availability) => availability.qty);

              const totalAvailableQuantity = eligibleAvailabilities.reduce((acc, curr) => acc + curr, 0);

              this.detectChanges();

              return totalAvailableQuantity;
            }
            return 0;
          })
        )
        .toPromise();

      return storeAvailability;
    } else if (this.deliveryType === DeliveryOption.DELIVERY) {
      const shippingAvailability = await this.productAvailabilityService
        .getShippingAvailability(this.book, this.book.product.ean, +this.selectedBranch.branchNumber, numberOfItems)
        .pipe(
          map((response) => {
            if (response && response.av) {
              const preferedAv = response.av.find((t) => t && t.preferred === 1);
              let preferredQty = 0;
              if (preferedAv) {
                this.currentAvailability = preferedAv;
                this._deliveryPrice = preferedAv.price.value.value;
                if (allowedAvailabilityStatusCodes(preferedAv.status)) {
                  preferredQty = preferedAv.qty;
                }
                this.detectChanges();
              }
              if (preferredQty) {
                return preferredQty;
              }
              return 0;
            }
            return 0;
          })
        )
        .toPromise();

      return shippingAvailability;
    }
    return of(numberOfItems).toPromise();
  }

  updateCart() {
    this.store.dispatch(
      new SetCartEntry(
        this.currentNumberOfItems,
        this.deliveryType,
        this._book.id,
        this.currentAvailability,
        this.selectedBranch.id,
        this._book.promoPoints
      )
    );
    this.closeModal();
  }

  itemsConfirmed() {
    if (!this._book || !this._book.id) {
      return;
    }
    this.store.dispatch(
      new SetCartEntry(
        this.currentNumberOfItems,
        this.deliveryType,
        this._book.id,
        this.currentAvailability,
        this.selectedBranch.id,
        this._book.promoPoints
      )
    );

    this.redirectRoute();
  }

  deleteProduct() {
    this.closeModal();
  }

  private redirectRoute() {
    if (this.customerAlredySet) {
      this.store.dispatch(new AddBreadcrumb(this.generateBreadcrumb(), 'shoppingCart', true));
      this.store.dispatch(new ChangeCurrentRoute('/cart/review'));
      this.router.navigate(['/cart/review']);
    } else {
      this.store.dispatch(new AddBreadcrumb(this.generateBreadcrumb(), 'customer', true));
      this.store.dispatch(new ChangeCurrentRoute('/customer/search'));
      this.router.navigate(['/customer/search']);
    }

    this.closeModal();
  }

  private generateBreadcrumb(): Breadcrumb {
    if (this.customerAlredySet) {
      return {
        name: 'Warenkorb',
        path: '/cart/review',
      };
    } else {
      return {
        name: 'Kundensuche',
        path: '/customer/search',
      };
    }
  }

  // COMMON
  openDialog() {
    this.currentNumberOfItems = 1;
    this.modalService.open(this.id);
  }

  closeModal(dialogSubmited: boolean = false) {
    this.closed.emit(dialogSubmited);
    this.modalService.close(this.id);
    this.defaultValues();
    this.stepOne = true;
  }

  switchSteps(reset: boolean = false) {
    if (reset) {
      this.defaultValues();
    }

    this.stepOne = !this.stepOne;
  }

  private defaultValues() {
    this.displayItemsNumber = 1;
    this.currentPoints = this.book.promoPoints + '';
  }

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

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