import { Selector, StateContext, createSelector } from '@ngxs/store';
import { CustomerState, CustomerStateModel } from '../state/customer.state';
import { BreadcrumbsState, BreadcrumbsStateModel } from '../state/breadcrumbs.state';
import { ProcessStateModel, ProcessState } from '../state/process.state';
import { EditCustomerData } from '../../models/edit-customer.model';
import { CartState, CartStateModel } from '../state/cart.state';
import { ProcessCart } from '../../models/process-cart.model';
import { FilterState, FilterStateModel } from '../state/filter.state';
import { Filter } from '../../models/filter.model';
import { CustomerSearchResult } from '../../models/customer-search-result.model';
import { ProductStateModel, ProductState } from '../state/product.state';
import { ProductSearchResult } from '../../models/product-search-result.model';
import { Breadcrumb } from '../../models/breadcrumb.model';
import { AppState, AppStateModel } from '../state/app.state';
import { CartEntryStateModel, CartEntryState } from '../state/cart-entry.state';
import { BranchesStateModel, BranchState } from '../state/branches.state';
import { Cart } from '../../models/cart.model';
import { DisplayOrderDTO, DisplayOrderItemDTO } from '@swagger/oms';
import { DeliveryType, DeliveryOption } from '../../models/delivery-option.model';
import { OrderItem } from '../../models/order-item';
import { ProcessCartConfirmed } from '../../models/process-cart-confirmed.model';
import { User } from '../../models/user.model';
import { FilterItem } from '../../models/filter-item.model';
import { CollectingShelfState, CollectingShelfStateModel } from '../state/collecting-shelf.state';
import { mapToIterable, undefinedToEmptyStringOrValue } from '../../utils/app.utils';
import { CollectingShelfOrder } from '../../models/collecting-shelf-order.model';
import { ItemDTO } from '@swagger/cat';
import { Process } from '../../models/process.model';
import { isNullOrUndefined } from 'util';
import { FilterType } from '../../models/filter-type.enum';
import { BranchProcessState, BranchProcessStateModel } from '../state/branch-process.state';
import { ModuleSwitcher } from '../../models/app-switcher.enum';
import { BranchProcess } from '../../models/branch-process.model';

export class SharedSelectors {
  @Selector([AppState, ProcessState, BranchProcessState])
  static getCurrentProcess(
    state: AppStateModel,
    processState: ProcessStateModel,
    branchProcessState: BranchProcessStateModel
  ): BranchProcess | Process {
    const activeModule = state.activeModule;
    if (activeModule === ModuleSwitcher.Branch) {
      return branchProcessState.branchProcess;
    }
    return processState.processes[state.currentProcesssId];
  }

  @Selector([AppState, ProcessState, CustomerState, BreadcrumbsState])
  static getCustomerEditData(
    state: AppStateModel,
    process: ProcessStateModel,
    customer: CustomerStateModel,
    breadcrumbs: BreadcrumbsStateModel
  ): EditCustomerData {
    const currentProcessId = state.currentProcesssId;
    const currentProcess = process.processes[currentProcessId];

    if (currentProcess && currentProcessId && breadcrumbs.activeCrumbs && breadcrumbs.activeCrumbs[currentProcessId]) {
      const activeCrumbs = breadcrumbs.activeCrumbs[currentProcessId];
      const currentBreadcrumbs = breadcrumbs.processesBreadcrumbs[activeCrumbs].find((t) => t.processId === currentProcessId);
      const detailsCustomer = customer.customers[currentProcess.detailsCustomer];
      return {
        processId: currentProcessId,
        customer: detailsCustomer,
        breadcrumbs: currentBreadcrumbs ? currentBreadcrumbs.breadcrumbs : null,
      };
    }

    return null;
  }

  @Selector([AppState, ProcessState, CustomerState])
  static getActiveCustomer(state: AppStateModel, process: ProcessStateModel, customer: CustomerStateModel): User {
    const currentProcessId = state.currentProcesssId;
    const currentProcess = process.processes[currentProcessId];

    if (currentProcess) {
      return customer.customers[currentProcess.activeCustomer];
    }

    return null;
  }

  @Selector([AppState, ProcessState, CustomerState])
  static getFinishedOrderCustomer(state: AppStateModel, process: ProcessStateModel, customer: CustomerStateModel): User {
    const currentProcessId = state.currentProcesssId;
    const currentProcess = process.processes[currentProcessId];

    if (currentProcess) {
      return customer.customers[currentProcess.finishedOrderCustomer];
    }

    return null;
  }

  @Selector([ProcessState, CartState, CartEntryState])
  static getCartTabData(process: ProcessStateModel, cartState: CartStateModel, cartEntriesState: CartEntryStateModel) {
    return (processId: number) => {
      if (process && processId) {
        const currentProcess = process.processes[processId];
        let quantity = 0;
        if (currentProcess === undefined) {
          return;
        }
        const cartId = currentProcess.cartId;
        const cartEntries = cartState.carts[cartId];

        if (cartEntries) {
          cartEntries.forEach((cartEntryId: number) => {
            const cartEntry = cartEntriesState.cartEntries[cartEntryId];
            quantity += cartEntry.quantity;
          });
        }

        return quantity;
      }

      return 0;
    };
  }

  @Selector([AppState, ProcessState, CartState, CartEntryState])
  static cartHasItemsFor(state: AppStateModel, process: ProcessStateModel, cart: CartStateModel, cartEntry: CartEntryStateModel) {
    return (option: DeliveryOption) => {
      let found = false;
      const currentProcessId = state.currentProcesssId;
      if (currentProcessId) {
        const currentProcess = process.processes[currentProcessId];
        if (currentProcess && currentProcess.cartId) {
          const cartEntryIds = cart.carts[currentProcess.cartId];
          if (cartEntryIds && cartEntryIds.hasItems()) {
            cartEntryIds.forEach((entryId) => {
              if (entryId) {
                const entry = cartEntry.cartEntries[entryId];
                if (entry.deliveryType === option) {
                  found = true;
                  return;
                }
              }
            });
          }
        }
      }
      return found;
    };
  }

  @Selector([AppState, ProcessState, CartState, CartEntryState])
  static cartHasDownloadForProcess(state: AppStateModel, process: ProcessStateModel, cart: CartStateModel, cartEntry: CartEntryStateModel) {
    let found = false;
    const currentProcessId = state.currentProcesssId;
    if (currentProcessId && process && process.processes) {
      const currentProcess = process.processes[currentProcessId];
      if (currentProcess && currentProcess.cartId) {
        const cartEntryIds = cart.carts[currentProcess.cartId];
        if (cartEntryIds && cartEntryIds.hasItems()) {
          cartEntryIds.forEach((entryId) => {
            if (entryId) {
              const entry = cartEntry.cartEntries[entryId];
              if (entry.deliveryType === DeliveryOption.DOWNLOAD) {
                found = true;
                return;
              }
            }
          });
        }
      }
    }
    return { status: found, currentProcessId: currentProcessId };
  }

  @Selector([AppState, ProcessState, CartState])
  static cartEntriesOfCurrentProcessCart(state: AppStateModel, process: ProcessStateModel, cart: CartStateModel) {
    const currentProcessId = state.currentProcesssId;
    if (currentProcessId) {
      const currentProcess = process.processes[currentProcessId];
      if (currentProcess && currentProcess.cartId) {
        const currentCart = cart.carts[currentProcess.cartId];
        return currentCart;
      }
    }
  }

  @Selector([AppState, ProcessState, CartState, CartEntryState, ProductState])
  static cartHasDowloads(
    state: AppStateModel,
    process: ProcessStateModel,
    cart: CartStateModel,
    cartEntry: CartEntryStateModel,
    product: ProductStateModel
  ): number[] {
    const currentProcessId = state.currentProcesssId;
    const response: number[] = [];
    if (currentProcessId) {
      const currentProcess = process.processes[currentProcessId];
      if (currentProcess && currentProcess.cartId) {
        const cartEntryIds = cart.carts[currentProcess.cartId];
        if (cartEntryIds && cartEntryIds.hasItems()) {
          cartEntryIds.forEach((entryId) => {
            if (entryId) {
              const entry = cartEntry.cartEntries[entryId];
              if (entry && entry.bookId) {
                const currentProcessArticles = product.products[currentProcessId];
                if (currentProcessArticles) {
                  if (currentProcessArticles && currentProcessArticles[entry.bookId]) {
                    const article = currentProcessArticles[entry.bookId];
                    if (article && article.product && article.product.format) {
                      if (article.product.format === 'EB' || article.product.format === 'DL') {
                        response.push(article.id);
                      }
                    }
                  }
                }
              }
            }
          });
        }
      }
    }
    return response;
  }

  @Selector([AppState, ProcessState, CartState, CartEntryState, ProductState])
  static cartHasItems(state: AppStateModel, process: ProcessStateModel, cart: CartStateModel, cartEntry: CartEntryStateModel): boolean {
    const currentProcessId = state.currentProcesssId;
    let response = false;
    if (currentProcessId) {
      const currentProcess = process.processes[currentProcessId];
      if (currentProcess && currentProcess.cartId) {
        const cartEntryIds = cart.carts[currentProcess.cartId];
        if (cartEntryIds && cartEntryIds.hasItems()) {
          cartEntryIds.forEach((entryId) => {
            if (entryId) {
              const entry = cartEntry.cartEntries[entryId];
              if (entry && entry.bookId) {
                response = true;
              }
            }
          });
        }
      }
    }
    return response;
  }

  @Selector([AppState, ProcessState, CartState, CustomerState, ProductState, CartEntryState, BranchState])
  static getCart(
    state: AppStateModel,
    process: ProcessStateModel,
    cartState: CartStateModel,
    customer: CustomerStateModel,
    productState: ProductStateModel,
    cartEntriesState: CartEntryStateModel,
    branchState: BranchesStateModel
  ): ProcessCart {
    const processId = state.currentProcesssId;
    const currentProcess = process.processes[processId];

    if (currentProcess) {
      // get all data
      const cartData: Cart[] = [];
      const cartId = currentProcess.cartId;
      const cartEntries = cartState.carts[cartId];
      const products = productState.products[processId];
      const currentCustomer = customer.customers[currentProcess.activeCustomer];
      const branches = branchState.branches;
      let promotionPoints = 0;

      // Prepare object for components
      cartEntries.forEach((cartEntryId: number) => {
        const cartEntry = cartEntriesState.cartEntries[cartEntryId];
        const book = products[cartEntry.bookId];
        const branch = branches[cartEntry.branch] ? branches[cartEntry.branch] : null;
        promotionPoints += book.promoPoints * cartEntry.quantity;
        cartData.push({
          cartId: cartId,
          cartEntryId: cartEntryId,
          book: book,
          quantity: cartEntry.quantity,
          deliveryType: cartEntry.deliveryType,
          branch: branch,
          orderTypeChanged: cartEntry.orderTypeChanged,
          avaMessage: cartEntry.avaMessage,
          pickUpPrice: cartEntry.pickUpPrice,
          deliveryPrice: cartEntry.deliveryPrice,
          downloadPrice: cartEntry.downloadPrice,
          deliveryDate: cartEntry.deliveryDate,
          pickUpDate: cartEntry.pickUpDate,
        });
      });

      return {
        cart: cartData,
        customer: currentCustomer,
        promotionPoints: promotionPoints,
      };
    }

    return null;
  }

  @Selector([AppState, ProcessState, CustomerState])
  static currentCustomer(state: AppStateModel, process: ProcessStateModel, customer: CustomerStateModel): User {
    const currentProcess = process.processes[state.currentProcesssId];
    if (!currentProcess) {
      return;
    }
    return customer.customers[currentProcess.activeCustomer];
  }

  @Selector([AppState, ProcessState])
  static getCurrentCartId(appState: AppStateModel, processState: ProcessStateModel) {
    const processId = appState.currentProcesssId;
    if (processId) {
      const currentProcess = processState.processes[processId];
      if (currentProcess) {
        return currentProcess.cartId;
      }
    }
    return null;
  }

  @Selector([AppState, FilterState])
  static getSelectedFilterItems(state: AppStateModel, filterState: FilterStateModel): Filter[] {
    const currentProcessId = state.currentProcesssId;
    if (currentProcessId) {
      const filterType = filterState.filterType[currentProcessId];
      const processesSelectedFilters =
        filterType === FilterType.Negative ? filterState.negativeProcessesSelectedFilters : filterState.processesSelectedFilters;
      const currentProcessSelectedFilters = processesSelectedFilters.find((f) => f.processId === currentProcessId);
      if (currentProcessSelectedFilters) {
        const filter: Filter[] = [];
        currentProcessSelectedFilters.selectedFilters.map((f) => {
          if (f.items && f.items.filter((t) => t.selected === true) && f.items.filter((t) => t.selected === true).length > 0) {
            filter.push(f);
          }
        });

        return filter;
      }
      return [];
    }
    return [];
  }

  @Selector([AppState, FilterState])
  static getProcessFilters(state: AppStateModel, filterState: FilterStateModel): Filter[] {
    const currentProcessId = state.currentProcesssId;

    if (currentProcessId) {
      const filterType = filterState.filterType[currentProcessId];
      const processesSelectedFilters =
        filterType === FilterType.Negative ? filterState.negativeProcessesSelectedFilters : filterState.processesSelectedFilters;
      const currentProcessSelectedFilters = processesSelectedFilters.find((f) => f.processId === currentProcessId);
      return currentProcessSelectedFilters ? currentProcessSelectedFilters.selectedFilters : null;
    }
  }

  @Selector([AppState, ProcessState])
  static getCurrentSearch(state: AppStateModel, process: ProcessStateModel) {
    const processId = state.currentProcesssId;
    const currentProcess = process.processes[processId];

    if (currentProcess) {
      return currentProcess.search;
    }
  }

  @Selector([AppState, ProcessState, CustomerState, BreadcrumbsState])
  static getSearchedCustomers(state: AppStateModel, process: ProcessStateModel, customer: CustomerStateModel): CustomerSearchResult {
    const processId = state.currentProcesssId;
    const currentProcess = process.processes[processId];

    if (currentProcess) {
      const customers = currentProcess.customerIds.map((t) => {
        return customer.customers[t];
      });

      return {
        processId,
        customers,
      };
    }

    return null;
  }

  @Selector([AppState, ProductState])
  static getSearchedProducts(state: AppStateModel, products: ProductStateModel): ProductSearchResult {
    const currentProcessId = state.currentProcesssId;

    if (currentProcessId) {
      const currentProducts = products.products[currentProcessId];

      return {
        itemsDTO: currentProducts ? currentProducts : null,
      };
    }

    return null;
  }

  @Selector([AppState, BreadcrumbsState])
  static getBreadcrumbs(state: AppStateModel, breadcrumbs: BreadcrumbsStateModel): Breadcrumb[] {
    const activeModule = state.activeModule;
    const currentProcessId = activeModule === ModuleSwitcher.Customer ? state.currentProcesssId : -1;

    if (currentProcessId && breadcrumbs.activeCrumbs && breadcrumbs.activeCrumbs[currentProcessId]) {
      const activeCrumbs = breadcrumbs.activeCrumbs[currentProcessId];
      const currentBreadcrumbs = breadcrumbs.processesBreadcrumbs[activeCrumbs].find((t) => t.processId === currentProcessId);
      return currentBreadcrumbs ? currentBreadcrumbs.breadcrumbs : null;
    }

    return null;
  }

  @Selector([AppState, BreadcrumbsState])
  static getPreviousRoute(state: AppStateModel, breadcrumbs: BreadcrumbsStateModel): { crumb: string; path: string } {
    const activeModule = state.activeModule;
    const currentProcessId = activeModule === ModuleSwitcher.Customer ? state.currentProcesssId : -1;

    if (currentProcessId && breadcrumbs.previusMenuPath && breadcrumbs.previusMenuPath[currentProcessId]) {
      const previousPath = breadcrumbs.previusMenuPath[currentProcessId];
      if (previousPath && previousPath[previousPath.length - 1]) {
        return previousPath[previousPath.length - 1];
      }
    }

    return null;
  }

  @Selector([AppState, ProcessState])
  static getConfirmedCart(state: AppStateModel, process: ProcessStateModel): ProcessCartConfirmed {
    const processId = state.currentProcesssId;
    const currentProcess = process.processes[processId];

    if (currentProcess) {
      try {
        // get all data
        const cartData: OrderItem[] = [];
        const orders = currentProcess.finishedOrder;
        const orderIds: string[] = [];
        let totalSum = 0;
        let totalQuantity = 0;
        let promotionPoints = 0;
        let targetBranchName = '';
        let targetBranchAddress = '';

        if (orders && orders.displayOrder) {
          orders.displayOrder.forEach((order: DisplayOrderDTO) => {
            orderIds.push(order.orderNumber);
            order.items.forEach((item: DisplayOrderItemDTO) => {
              if (order.orderType === DeliveryType['Branch']) {
                targetBranchName = order.targetBranch.name;
                // tslint:disable-next-line: max-line-length
                targetBranchAddress = `${undefinedToEmptyStringOrValue(order.targetBranch.address.street)} ${undefinedToEmptyStringOrValue(
                  order.targetBranch.address.streetNumber
                )}, ${undefinedToEmptyStringOrValue(order.targetBranch.address.zipCode)} ${undefinedToEmptyStringOrValue(
                  order.targetBranch.address.city
                )}`;
              }

              totalSum += item.price.value.value * item.quantity;
              totalQuantity += item.quantity;
              if (item.promotion) {
                promotionPoints += item.quantity * item.promotion.points;
              }
              const isDownload = item.product.format === 'EB' || item.product.format === 'DL';
              const orderItem: OrderItem = {
                id: +item.product.catalogProductNumber,
                orderId: order.id,
                orderNumber: order.orderNumber,
                orderType: order.orderType,
                book: item.product,
                quantity: item.quantity,
                currency: item.price.value.currency,
                price: item.price.value.value,
                imgUrl: item.product.ean,
                isTakeNow: item.subsetItems[0].supplierLabel === 'F' ? true : false,
                isDownload: isDownload,
                orderDate: item.orderDate,
                targetBranchName: targetBranchName,
                targetBranchAddress: targetBranchAddress,
              };

              if (order.orderType === DeliveryType['ShippingAddress']) {
                orderItem.deliveryDate = item.subsetItems[0].estimatedShippingDate;
              }

              if (order.orderType === DeliveryType['Branch']) {
                orderItem.pickUpDate = item.subsetItems[0].estimatedShippingDate;
              }

              cartData.push(orderItem);
            });
          });

          const takeNowItems = cartData.filter((item: OrderItem) => item.isTakeNow);
          const pickUpItems = cartData.filter((item: OrderItem) => !item.isTakeNow && item.orderType === DeliveryType['Branch']);

          // Prepare object for components
          return {
            cart: cartData,
            failedItemIds: orders.failedItemIds,
            totalSum: totalSum,
            totalQuantity: totalQuantity,
            cartId: currentProcess.cartId,
            orderIds: orderIds,
            isTakeNowOnly: takeNowItems && cartData && takeNowItems.length === cartData.length ? true : false,
            isPickUpOnly: pickUpItems && cartData && pickUpItems.length === cartData.length ? true : false,
            promotionPoints: promotionPoints,
          };
        }
      } catch (error) {
        console.error(error);
      }
    }

    return null;
  }

  @Selector([AppState])
  static getAppState(state: AppStateModel) {
    const currentProcessId = state.currentProcesssId;
    const currentProcessIds = state.processIds;

    return {
      currentProcessId: currentProcessId,
      currentProcessIds: currentProcessIds,
    };
  }

  @Selector([AppState, FilterState])
  static getFilters(appState: AppStateModel, state: FilterStateModel): Filter[] {
    const currenProcessId = appState.currentProcesssId;
    const filterType = state.filterType[currenProcessId];
    const processesSelectedFilters =
      filterType === FilterType.Negative ? state.negativeProcessesSelectedFilters : state.processesSelectedFilters;
    const processFilters = processesSelectedFilters[currenProcessId];
    return state.filters.map((filter) => {
      return <Filter>{
        ...filter,
        items: filter.items.map((item) => {
          if (processFilters) {
            const selectedFilter = processFilters.selectedFilters.find((f) => f.id === filter.id);
            if (selectedFilter) {
              const selectedItem = selectedFilter.items.find((i) => i.id === item.id);
              if (selectedItem) {
                return <FilterItem>{ ...item, selected: selectedItem.selected };
              } else {
                return <FilterItem>{ ...item, selected: false };
              }
            } else {
              return <FilterItem>{ ...item, selected: false };
            }
          } else {
            return <FilterItem>{ ...item, selected: false };
          }
        }),
      };
    });
  }

  static getFilterIndex(index: number) {
    const val = index;
    return createSelector(
      [AppState, FilterState],
      (appState: AppStateModel, state: FilterStateModel): Filter => {
        const currenProcessId = appState.currentProcesssId;
        const filterType = state.filterType[currenProcessId];
        return filterType === FilterType.Negative
          ? state.negativeDropdownFilters[currenProcessId][val]
          : state.dropdownFilters[currenProcessId][val];
      }
    );
  }

  @Selector([AppState, FilterState])
  static getFilterCount(appState: AppStateModel, state: FilterStateModel) {
    const currenProcessId = appState.currentProcesssId;
    const filterType = state.filterType[currenProcessId];
    return filterType === FilterType.Negative
      ? state.negativeDropdownFilters[currenProcessId]
        ? state.negativeDropdownFilters[currenProcessId].length
        : 0
      : state.dropdownFilters[currenProcessId]
      ? state.dropdownFilters[currenProcessId].length
      : 0;
  }

  @Selector([AppState, FilterState])
  static getDropDownFilters(appState: AppStateModel, state: FilterStateModel) {
    const currenProcessId = appState.currentProcesssId;
    const filterType = state.filterType[currenProcessId];
    return filterType === FilterType.Negative
      ? state.negativeDropdownFilters[currenProcessId]
        ? state.negativeDropdownFilters[currenProcessId]
        : null
      : state.dropdownFilters[currenProcessId]
      ? state.dropdownFilters[currenProcessId]
      : null;
  }

  @Selector([AppState, CollectingShelfState])
  static getIterableOrderForCurrentProcess(appState: AppStateModel, orderState: CollectingShelfStateModel): CollectingShelfOrder[] {
    const currenProcessId = appState.currentProcesssId;
    if (!currenProcessId) {
      return [];
    }
    if (!orderState) {
      return [];
    }
    if (!orderState.orders) {
      return [];
    }
    if (!orderState.orders[currenProcessId]) {
      return [];
    }
    return mapToIterable(orderState.orders[currenProcessId]);
  }

  @Selector([AppState, ProcessState])
  static getProcessSelectedItem(appState: AppStateModel, process: ProcessStateModel): ItemDTO {
    const currenProcessId = appState.currentProcesssId;
    if (!currenProcessId) {
      return;
    }
    return process.processes[currenProcessId].selectedItem;
  }

  @Selector([AppState, ProcessState, CustomerState])
  static getCurrentProcessDetailsCustomer(appState: AppStateModel, processes: ProcessStateModel, customers: CustomerStateModel) {
    const currentProcessId = appState.currentProcesssId;
    if (!currentProcessId) {
      return;
    }
    const currentProcess: Process = processes.processes[currentProcessId];
    if (!currentProcess) {
      return;
    }
    const customer = customers.customers[currentProcess.detailsCustomer];
    return customer;
  }

  @Selector([AppState, FilterState])
  static getFilterType(appState: AppStateModel, filterState: FilterStateModel) {
    const currentProcessId = appState.currentProcesssId;
    if (!currentProcessId) {
      return;
    }
    return filterState.filterType[currentProcessId];
  }

  @Selector([AppState])
  static getActiveModule(appState: AppStateModel) {
    const activeModule = appState.activeModule;

    const module = {
      id: activeModule,
      name: !activeModule ? 'Customer' : 'Branch',
    };

    return module;
  }
}
