import { Injectable } from '@angular/core';
import {
  ListResponseArgsOfOrderItemListItemDTO,
  OrderDTO,
  OrderItemDTO,
  OrderItemSubsetDTO,
  ValueTupleOfOrderItemSubsetDTOAndOrderItemSubsetDTO,
  StatusValues,
  ReceiptListItemDTO,
  OrderItemListItemDTO,
  OrderService,
  ReceiptService,
  VATDTO,
  SupplierDTO,
  QueryTokenDTO,
  AutocompleteTokenDTO,
  AbholfachService,
} from '@swagger/oms';
import { map, filter } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { ShelfMapping } from '../mappings/shelf.mapping';
import { CollectingShelfOrder } from '../models/collecting-shelf-order.model';
import { DatePipe } from '@angular/common';
import { BACKEND_API_TIMESTAMP_FORMAT } from '../utils/app.formats';
import { isNullOrUndefined } from 'util';
import { Store } from '@ngxs/store';
import { CollectingShelfSelectors } from '../store/selectors/collecting-shelf.selector';

@Injectable({
  providedIn: 'root',
})
export class CollectingShelfService {
  constructor(
    private abholfachService: AbholfachService,
    private omsService: OrderService,
    private abholfach: AbholfachService,
    private mapper: ShelfMapping,
    public datepipe: DatePipe,
    private receiptService: ReceiptService,
    private store: Store
  ) {}

  searchShelf(
    input: string,
    branchNumber: string,
    skip: number = 0,
    take: number = 0
  ): Observable<{ result: CollectingShelfOrder[]; hits: number }> {
    const params = <QueryTokenDTO>{
      input: {
        qs: input,
      },
      branchNumber,
      skip: skip,
      take: take,
    };

    return this.abholfach.AbholfachWarenausgabe(params).pipe(
      map((response) => {
        if (response.error) {
          throw new Error(response.message);
        }
        return {
          result: this.mapper.fromOrderItemListItemDTOArrayToCollectingShelfOrder(response.result, this, skip === 0),
          hits: response.hits,
        };
      })
    );
  }

  searchShelfCached() {
    return this.store.select(CollectingShelfSelectors.getCachedResults).pipe(filter((data) => !isNullOrUndefined(data)));
  }

  searchShelfHasResults(input: string, branchNumber: string): Observable<number> {
    const params = <QueryTokenDTO>{
      input: {
        qs: input,
      },
      branchNumber,
    };
    return this.abholfach.AbholfachWarenausgabe(params).pipe(
      map((response) => {
        if (response.error) {
          throw new Error(response.message);
        }
        return response.hits ? response.hits : 0;
      })
    );
  }

  searchWarenausgabe({
    input,
    branchNumber,
    skip = 0,
    take = 10,
    hitsOnly = false,
    selectedFilters = {},
  }): Observable<ListResponseArgsOfOrderItemListItemDTO> {
    const params = <QueryTokenDTO>{
      input: {
        qs: input,
      },
      filter: selectedFilters,
      skip,
      take,
      hitsOnly,
      branchNumber,
    };
    return this.abholfach.AbholfachWarenausgabe(params).pipe(
      map((response) => {
        if (response.error) {
          throw new Error(response.message);
        }
        return response;
      })
    );
  }

  searchWarenausgabeAutocomplete(autocompleteTokenDTO: AutocompleteTokenDTO) {
    return this.abholfach.AbholfachWarenausgabeAutocomplete(autocompleteTokenDTO);
  }

  getOrderByOrderId(orderId: number): Observable<OrderDTO> {
    return this.omsService.OrderGetOrder(orderId).pipe(
      map((response) => {
        if (response.error) {
          throw new Error(response.message);
        }
        // return {
        //   ...response.result,
        //   items: response.result.items.filter(i => i.data.subsetItems.findIndex(t => !isNullOrUndefined(t.data.compartmentCode)) === -1),
        // };
        return response.result;
      })
    );
  }

  getOrderItemByOrderItemId(orderItemId: number): Observable<OrderItemDTO> {
    return this.omsService.OrderGetOrderItem(orderItemId).pipe(
      map((response) => {
        if (response.error) {
          throw new Error(response.message);
        }
        return response.result;
      })
    );
  }

  getOrderItemSubsetByOrderItemSubsetId(orderItemSubsetId: number): Observable<OrderItemSubsetDTO> {
    return this.omsService.OrderGetOrderItemSubset(orderItemSubsetId).pipe(
      map((response) => {
        if (response.error) {
          throw new Error(response.message);
        }
        return response.result;
      })
    );
  }

  getOrdersByCompartmentNumber(compartmentNumbers: string[], orderId: number): Observable<OrderDTO> {
    return this.omsService.OrderGetOrdersByCompartment(compartmentNumbers).pipe(
      map((response) => {
        if (response.error) {
          throw new Error(response.message);
        }
        return response.result ? response.result.find((o) => +o.id === +orderId) : null;
      })
    );
  }

  changeOrderArticleStatus(
    orderId: number,
    orderItemId: number,
    orderItemSubsetId: number,
    quantity: number,
    processingStatus: number,
    compartmentInfo?: string
  ): Observable<ValueTupleOfOrderItemSubsetDTOAndOrderItemSubsetDTO> {
    const params = <OrderService.OrderChangeStatusParams>{
      orderId,
      orderItemId,
      orderItemSubsetId,
      data: <StatusValues>{
        quantity,
        processingStatus,
        compartmentInfo,
      },
    };
    return this.omsService.OrderChangeStatus(params).pipe(
      map((response) => {
        if (response.error) {
          throw new Error(response.message);
        }
        return response.result;
      })
    );
  }

  editOrderArticleStatus(
    orderId: number,
    orderItemId: number,
    orderItemSubsetId: number,
    quantity: number,
    processingStatus: number
  ): Observable<{ itemId: number; result: OrderItemSubsetDTO }> {
    const params = <OrderService.OrderChangeStatusParams>{
      orderId,
      orderItemId,
      orderItemSubsetId,
      data: <StatusValues>{
        quantity,
        processingStatus,
      },
    };
    return this.omsService.OrderChangeStatus(params).pipe(
      map((response) => {
        if (response.error) {
          throw new Error(response.message);
        }
        return { result: response.result.item1, itemId: orderItemId };
      })
    );
  }

  extendPickUpPeriod(orderId: number, orderItemId: number, orderItemSubsetId: number, date: Date): Observable<OrderItemSubsetDTO> {
    const compartmentStop = this.datepipe.transform(date, BACKEND_API_TIMESTAMP_FORMAT);
    const params = <OrderService.OrderPatchOrderItemSubsetParams>{
      orderId,
      orderItemId,
      orderItemSubsetId,
      orderItemSubset: <OrderItemSubsetDTO>{
        compartmentStop: compartmentStop,
      },
    };
    return this.omsService.OrderPatchOrderItemSubset(params).pipe(
      map((response) => {
        if (response.error) {
          throw new Error(response.message);
        }
        return response.result;
      })
    );
  }

  updateEstimatedShippingDate(orderId: number, orderItemId: number, orderItemSubsetId: number, date: Date): Observable<OrderItemSubsetDTO> {
    const estimatedShippingDate = this.datepipe.transform(date, BACKEND_API_TIMESTAMP_FORMAT);
    const params = <OrderService.OrderPatchOrderItemSubsetParams>{
      orderId,
      orderItemId,
      orderItemSubsetId,
      orderItemSubset: <OrderItemSubsetDTO>{
        estimatedShippingDate: estimatedShippingDate,
      },
    };
    return this.omsService.OrderPatchOrderItemSubset(params).pipe(
      map((response) => {
        if (response.error) {
          throw new Error(response.message);
        }
        return response.result;
      })
    );
  }

  patchOrderItem(orderId: number, orderItemId: number, orderItem: OrderItemDTO) {
    const params = <OrderService.OrderPatchOrderItemParams>{
      orderId,
      orderItemId,
      orderItem,
    };
    return this.omsService.OrderPatchOrderItem(params).pipe(
      map((response) => {
        if (response.error) {
          throw new Error(response.message);
        }
        return response.result;
      })
    );
  }

  patchOrderItemSubset(
    orderId: number,
    orderItemId: number,
    orderItemSubsetId: number,
    orderItemSubset: OrderItemSubsetDTO
  ): Observable<OrderItemSubsetDTO> {
    const params = <OrderService.OrderPatchOrderItemSubsetParams>{
      orderId,
      orderItemId,
      orderItemSubsetId,
      orderItemSubset,
    };
    return this.omsService.OrderPatchOrderItemSubset(params).pipe(
      map((response) => {
        if (response.error) {
          throw new Error(response.message);
        }
        return response.result;
      })
    );
  }

  patchOrderItemSubsetRreturningItemId(
    orderId: number,
    orderItemId: number,
    orderItemSubsetId: number,
    orderItemSubset: OrderItemSubsetDTO
  ): Observable<{ itemId: number; result: OrderItemSubsetDTO }> {
    const params = <OrderService.OrderPatchOrderItemSubsetParams>{
      orderId,
      orderItemId,
      orderItemSubsetId,
      orderItemSubset,
    };
    return this.omsService.OrderPatchOrderItemSubset(params).pipe(
      map((response) => {
        if (response.error) {
          throw new Error(response.message);
        }
        return { itemId: orderItemId, result: response.result };
      })
    );
  }

  getReceipt(orderId: number): Observable<ReceiptListItemDTO[]> {
    const params = <ReceiptService.ReceiptReceiptsByOrderIdParams>{
      orderId,
      receiptType: 64,
    };
    return this.receiptService.ReceiptReceiptsByOrderId(params).pipe(
      map((response) => {
        if (response.error) {
          throw new Error(response.message);
        }
        return response.result;
      })
    );
  }

  getMessageText(code: string, supplierId: number) {
    const params = <OrderService.OrderGetStockStatusCodesParams>{
      supplierId,
      code,
    };
    return this.omsService.OrderGetStockStatusCodes(params).pipe(
      map((response) => {
        if (response.error) {
          throw new Error(response.message);
        }
        return response.result && response.result[0] ? response.result[0].supplierDescription : null;
      })
    );
  }

  getGrossPriceVats() {
    return this.omsService.OrderGetVATs({}).pipe(
      map((response) => {
        if (response.error) {
          throw new Error(response.message);
        }
        return response.result as VATDTO[];
      })
    );
  }

  getSuppliers() {
    return this.omsService.OrderGetSuppilers({}).pipe(
      map((response) => {
        if (response.error) {
          throw new Error(response.message);
        }
        return response.result as SupplierDTO[];
      })
    );
  }

  processOrderCompartmentNumber(orders: OrderItemListItemDTO[], order: OrderItemListItemDTO, index: number, firstBatch: boolean) {
    const tempOrders = [...orders];
    const prevOrders = tempOrders.slice(0, index);
    let hideCompartmentNumber = false;
    if (index === 0 && !firstBatch) {
      const olderOrders = this.store.selectSnapshot(CollectingShelfSelectors.getShelfOrders);
      if (olderOrders && olderOrders.length > 0) {
        const filteredOrders = olderOrders.filter((t) => t.orderId === order.orderId);
        if (
          order.compartmentCode &&
          order.processingStatus &&
          filteredOrders &&
          filteredOrders.length > 0 &&
          filteredOrders[filteredOrders.length - 1] &&
          filteredOrders[filteredOrders.length - 1].compartmentCode &&
          filteredOrders[filteredOrders.length - 1].processingStatus &&
          filteredOrders[filteredOrders.length - 1].compartmentCode === order.compartmentCode &&
          filteredOrders[filteredOrders.length - 1].processingStatus === order.processingStatus
        ) {
          hideCompartmentNumber = true;
        }
      }
    } else {
      if (
        order.compartmentCode &&
        order.processingStatus &&
        prevOrders &&
        prevOrders[prevOrders.length - 1] &&
        prevOrders[prevOrders.length - 1].compartmentCode &&
        prevOrders[prevOrders.length - 1].processingStatus &&
        prevOrders[prevOrders.length - 1].compartmentCode === order.compartmentCode &&
        prevOrders[prevOrders.length - 1].processingStatus === order.processingStatus
      ) {
        hideCompartmentNumber = true;
      }
    }
    return hideCompartmentNumber;
  }
}
