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

@Injectable()
export class DomainOmsService {
  constructor(
    private orderService: OrderService,
    private receiptService: ReceiptService,
    private branchService: BranchService,
    private vatService: VATService,
    private stockStatusCodeService: StockStatusCodeService,
    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.branchService.BranchGetBranches({});
  }

  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.vatService.VATGetVATs({}).pipe(map((response) => response.result));
  }

  // ttl 4 Stunden
  @memorize({ ttl: 14400000 })
  getStockStatusCodes({ supplierId, eagerLoading = 0 }: { supplierId: number; eagerLoading?: number }) {
    return this.stockStatusCodeService.StockStatusCodeGetStockStatusCodes({ 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));
  }

  setPreferredPickUpDate({ data }: { data: { [key: string]: string } }) {
    return this.orderService.OrderSetPreferredPickUpDate({ data });
  }

  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,
      })),
    );
  }

  getOrderSource(orderId: number): Observable<string> {
    return this.getOrder(orderId).pipe(map((order) => order?.features?.orderSource));
  }

  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.updateOrder({ orderId, notificationChannels: changes.selected, communicationDetails });
  }

  updateOrder({
    orderId,
    notificationChannels,
    communicationDetails,
    firstName,
    lastName,
    organisation,
  }: {
    orderId: number;
    notificationChannels?: NotificationChannel;
    communicationDetails?: { email?: string; mobile?: string };
    lastName?: string;
    firstName?: string;
    organisation?: string;
  }) {
    const buyer: BuyerDTO = {};

    if (communicationDetails) {
      buyer.communicationDetails = { ...communicationDetails };
    }

    if (!!lastName || !!firstName) {
      buyer.lastName = lastName;
      buyer.firstName = firstName;
    }

    if (!!organisation && !!buyer.organisation) {
      buyer.organisation = {
        name: organisation,
      };
    }

    return this.orderService
      .OrderPatchOrder({
        orderId: orderId,
        order: {
          notificationChannels,
          buyer,
        },
      })
      .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[]>,
            ),
        ),
      );
  }
}
