import { Injectable } from '@angular/core';
import {
  ChangeStockStatusCodeValues,
  HistoryDTO,
  NotificationChannel,
  OrderCheckoutService,
  OrderDTO,
  OrderItemDTO,
  OrderItemSubsetDTO,
  OrderListItemDTO,
  OrderService,
  ReceiptService,
  StatusValues,
  ValueTupleOfLongAndReceiptTypeAndEntityDTOContainerOfReceiptDTO,
  ValueTupleOfOrderItemSubsetDTOAndOrderItemSubsetDTO,
} from '@swagger/oms';
import { memorize } from '@utils/common';
import { Observable } from 'rxjs';
import { map, mergeMap, shareReplay } from 'rxjs/operators';

@Injectable()
export class DomainOmsService {
  constructor(
    private orderService: OrderService,
    private receiptService: ReceiptService,
    private _orderCheckoutService: OrderCheckoutService
  ) {}

  getOrderItemsByCustomerNumber(customerNumber: string, skip: number): Observable<OrderListItemDTO[]> {
    return this.orderService
      .OrderGetOrdersByBuyerNumber({ buyerNumber: customerNumber, take: 20, skip })
      .pipe(map((orders) => orders.result));
  }

  getOrder(orderId: number): Observable<OrderDTO> {
    return this.orderService.OrderGetOrder(orderId).pipe(map((o) => o.result));
  }

  getBranches() {
    return this.orderService.OrderGetBranches({});
  }

  getHistory(orderItemSubsetId: number): Observable<HistoryDTO> {
    return this.orderService.OrderGetOrderItemStatusHistory({ orderItemSubsetId }).pipe(map((response) => response.result));
  }

  getReceipts(orderItemSubsetIds: number[]): Observable<ValueTupleOfLongAndReceiptTypeAndEntityDTOContainerOfReceiptDTO[]> {
    return this.receiptService
      .ReceiptGetReceiptsByOrderItemSubset({
        payload: {
          receiptType: (65 as unknown) as any,
          ids: orderItemSubsetIds,
          eagerLoading: 1,
        },
      })
      .pipe(map((response) => response.result));
  }

  getReorderReasons() {
    return this._orderCheckoutService.OrderCheckoutGetReorderReasons().pipe(map((response) => response.result));
  }

  @memorize()
  getVATs() {
    return this.orderService.OrderGetVATs({}).pipe(map((response) => response.result));
  }

  // ttl 4 Stunden
  @memorize({ ttl: 14400000 })
  getStockStatusCodes({ supplierId, eagerLoading = 0 }: { supplierId: number; eagerLoading?: number }) {
    return this.orderService.OrderGetStockStatusCodes({ supplierId, eagerLoading }).pipe(
      map((response) => response.result),
      shareReplay()
    );
  }

  patchOrderItem(payload: { orderItemId: number; orderId: number; orderItem: Partial<OrderItemDTO> }) {
    return this.orderService.OrderPatchOrderItem(payload).pipe(map((response) => response.result));
  }

  patchOrderItemSubset(payload: {
    orderItemSubsetId: number;
    orderItemId: number;
    orderId: number;
    orderItemSubset: Partial<OrderItemSubsetDTO>;
  }) {
    return this.orderService.OrderPatchOrderItemSubset(payload).pipe(map((response) => response.result));
  }

  patchComment({
    orderId,
    orderItemId,
    orderItemSubsetId,
    specialComment,
  }: {
    orderId: number;
    orderItemId: number;
    orderItemSubsetId: number;
    specialComment: string;
  }) {
    return this.orderService
      .OrderPatchOrderItemSubset({
        orderId,
        orderItemId,
        orderItemSubsetId,
        orderItemSubset: {
          specialComment,
        },
      })
      .pipe(map((response) => response.result));
  }

  changeOrderStatus(
    orderId: number,
    orderItemId: number,
    orderItemSubsetId: number,
    data: StatusValues
  ): Observable<ValueTupleOfOrderItemSubsetDTOAndOrderItemSubsetDTO> {
    return this.orderService
      .OrderChangeStatus({
        data,
        orderId,
        orderItemId,
        orderItemSubsetId,
      })
      .pipe(map((o) => o.result));
  }

  setEstimatedShippingDate(orderId: number, orderItemId: number, orderItemSubsetId: number, estimatedShippingDate: Date | string) {
    return this.orderService
      .OrderPatchOrderItemSubset({
        orderId,
        orderItemId,
        orderItemSubsetId,
        orderItemSubset: {
          estimatedShippingDate: estimatedShippingDate instanceof Date ? estimatedShippingDate.toJSON() : estimatedShippingDate,
        },
      })
      .pipe(map((response) => response.result));
  }

  setPickUpDeadline(orderId: number, orderItemId: number, orderItemSubsetId: number, pickUpDeadline: string) {
    return this.orderService
      .OrderPatchOrderItemSubset({
        orderId,
        orderItemId,
        orderItemSubsetId,
        orderItemSubset: {
          compartmentStop: pickUpDeadline,
        },
      })
      .pipe(map((response) => response.result));
  }

  changeOrderItemStatus(data: OrderService.OrderChangeStatusParams) {
    return this.orderService.OrderChangeStatus(data);
  }

  changeStockStatusCode(payload: ChangeStockStatusCodeValues[]) {
    return this.orderService.OrderChangeStockStatusCode(payload).pipe(map((response) => response.result));
  }

  orderAtSupplier({ orderId, orderItemId, orderItemSubsetId }: { orderId: number; orderItemId: number; orderItemSubsetId: number }) {
    return this._orderCheckoutService.OrderCheckoutOrderSubsetItemAtSupplier({
      orderId,
      orderItemId,
      orderItemSubsetId,
    });
  }

  getNotifications(orderId: number): Observable<{ selected: NotificationChannel; email: string; mobile: string }> {
    return this.getOrder(orderId).pipe(
      map((order) => ({
        selected: order.notificationChannels,
        email: order.buyer?.communicationDetails?.email,
        mobile: order.buyer?.communicationDetails?.mobile,
      }))
    );
  }

  updateNotifications(orderId: number, changes: { selected: NotificationChannel; email: string; mobile: string }) {
    const communicationDetails = {
      email: changes.email,
      mobile: changes.mobile,
    };

    if (!(changes.selected & 1)) {
      delete communicationDetails.email;
    }
    if (!(changes.selected & 2)) {
      delete communicationDetails.mobile;
    }

    return this.orderService
      .OrderPatchOrder({
        orderId: orderId,
        order: {
          notificationChannels: changes.selected,
          buyer: { communicationDetails },
        },
      })
      .pipe(map((res) => res.result));
  }

  generateNotifications({ orderId, taskTypes }: { orderId: number; taskTypes: string[] }) {
    return this.orderService.OrderRegenerateOrderItemStatusTasks({
      orderId,
      taskTypes,
    });
  }

  getCompletedTasks({
    orderId,
    orderItemId,
    orderItemSubsetId,
    take,
    skip,
  }: {
    orderId: number;
    orderItemId: number;
    orderItemSubsetId: number;
    take?: number;
    skip?: number;
  }): Observable<Record<string, Date[]>> {
    return this.orderService
      .OrderGetOrderItemSubsetTasks({ orderId, orderItemId, orderItemSubsetId, completed: new Date(0).toISOString(), take, skip })
      .pipe(
        map((res) =>
          res.result
            .sort((a, b) => new Date(b.completed).getTime() - new Date(a.completed).getTime())
            .reduce((data, result) => {
              (data[result.name] = data[result.name] || []).push(new Date(result.completed));
              return data;
            }, {} as Record<string, Date[]>)
        )
      );
  }
}
