import { CdkTextareaAutosize } from '@angular/cdk/text-field';
import {
  Component,
  ChangeDetectionStrategy,
  Input,
  Output,
  EventEmitter,
  ChangeDetectorRef,
  OnDestroy,
  OnInit,
  ViewChild,
} from '@angular/core';
import { UntypedFormControl } from '@angular/forms';
import { DomainOmsService, DomainReceiptService } from '@domain/oms';
import { ComponentStore } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';

import {
  OrderDTO,
  OrderItemListItemDTO,
  ReceiptDTO,
  ReceiptType,
} from '@generated/swagger/oms-api';
import { isEqual } from 'lodash';
import { combineLatest, NEVER, Subject, Observable } from 'rxjs';
import {
  catchError,
  filter,
  first,
  map,
  switchMap,
  withLatestFrom,
} from 'rxjs/operators';
import { CustomerOrderDetailsStore } from '../customer-order-details.store';
import { EnvironmentService } from '@core/environment';
import { getOrderItemRewardFeature } from '@isa/oms/data-access';

export interface CustomerOrderDetailsItemComponentState {
  orderItem?: OrderItemListItemDTO;
  order?: OrderDTO;
  quantity?: number;
  receipts?: ReceiptDTO[];
  selected?: boolean;
  more: boolean;
}

@Component({
  selector: 'page-customer-order-details-item',
  templateUrl: 'customer-order-details-item.component.html',
  styleUrls: ['customer-order-details-item.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  standalone: false,
})
export class CustomerOrderDetailsItemComponent
  extends ComponentStore<CustomerOrderDetailsItemComponentState>
  implements OnInit, OnDestroy
{
  @ViewChild('autosize') autosize: CdkTextareaAutosize;

  @Output()
  historyClick = new EventEmitter<OrderItemListItemDTO>();

  @Output()
  specialCommentChanged = new EventEmitter<void>();

  @Input()
  get orderItem() {
    return this.get((s) => s.orderItem);
  }
  set orderItem(orderItem: OrderItemListItemDTO) {
    if (!isEqual(this.orderItem, orderItem)) {
      // Remove Prev OrderItem from selected list
      this._store.selectOrderItem(this.orderItem, false);

      this.patchState({
        orderItem,
        quantity: orderItem?.quantity,
        receipts: [],
        more: false,
      });
      this.specialCommentControl.reset(orderItem?.specialComment);

      // Add New OrderItem to selected list if selected was set to true by its input
      if (this.get((s) => s.selected)) {
        this._store.selectOrderItem(this.orderItem, true);
      }
    }
  }

  @Input()
  get order() {
    return this.get((s) => s.order);
  }
  set order(order: OrderDTO) {
    this.patchState({ order });
  }

  readonly orderItem$ = this.select((s) => s.orderItem);

  notificationsSent$ = this.orderItem$.pipe(
    filter((oi) => !!oi),
    switchMap((oi) =>
      this._omsService
        .getCompletedTasks({
          orderId: oi.orderId,
          orderItemId: oi.orderItemId,
          orderItemSubsetId: oi.orderItemSubsetId,
          take: 4,
          skip: 0,
        })
        .pipe(catchError(() => NEVER)),
    ),
  );

  hasRewardPoints$ = this.orderItem$.pipe(
    map((orderItem) => getOrderItemRewardFeature(orderItem) !== undefined),
  );

  rewardPoints$ = this.orderItem$.pipe(
    map((orderItem) => getOrderItemRewardFeature(orderItem)),
  );

  canChangeQuantity$ = combineLatest([
    this.orderItem$,
    this._store.fetchPartial$,
  ]).pipe(
    map(
      ([item, partialPickup]) =>
        ([16, 8192].includes(item?.processingStatus) || partialPickup) &&
        item.quantity > 1,
    ),
  );

  get quantity() {
    return this.get((s) => s.quantity);
  }
  set quantity(quantity: number) {
    if (this.quantity !== quantity) {
      this.patchState({ quantity });
    }
  }

  readonly quantity$ = this.select((s) => s.quantity);

  @Input()
  get selected() {
    return this._store.selectedeOrderItemSubsetIds.includes(
      this.orderItem?.orderItemSubsetId,
    );
  }
  set selected(selected: boolean) {
    if (this.selected !== selected) {
      this.patchState({ selected });
      this._store.selectOrderItem(this.orderItem, selected);
    }
  }

  readonly selected$ = combineLatest([
    this.orderItem$,
    this._store.selectedeOrderItemSubsetIds$,
  ]).pipe(
    map(([orderItem, selectedItems]) =>
      selectedItems.includes(orderItem?.orderItemSubsetId),
    ),
  );

  @Output()
  selectedChange = new EventEmitter<boolean>();

  get selectable() {
    return (
      this._store.itemsSelectable &&
      this._store.items.length > 1 &&
      this._store.fetchPartial
    );
  }

  readonly selectable$ = combineLatest([
    this._store.items$,
    this._store.itemsSelectable$,
    this._store.fetchPartial$,
  ]).pipe(
    map(
      ([orderItems, selectable, fetchPartial]) =>
        orderItems.length > 1 && selectable && fetchPartial,
    ),
  );

  get receipts() {
    return this.get((s) => s.receipts);
  }
  set receipts(receipts: ReceiptDTO[]) {
    if (!isEqual(this.receipts, receipts)) {
      this.patchState({ receipts });
      this._store.updateReceipts(receipts);
    }
  }

  readonly receipts$ = this.select((s) => s.receipts);

  readonly receiptCount$ = this.select((s) => s.receipts?.length);

  specialCommentControl = new UntypedFormControl();

  more$ = this.select((s) => s.more);

  private _onDestroy$ = new Subject<void>();

  get isNative() {
    return this._environment.isNative();
  }

  constructor(
    private _store: CustomerOrderDetailsStore,
    private _domainReceiptService: DomainReceiptService,
    private _omsService: DomainOmsService,
    private _cdr: ChangeDetectorRef,
    private _environment: EnvironmentService,
  ) {
    super({
      more: false,
    });
  }

  // eslint-disable-next-line @typescript-eslint/no-empty-function
  ngOnInit() {}

  ngOnDestroy() {
    // Remove Prev OrderItem from selected list
    this._store.selectOrderItem(this.orderItem, false);
    this._onDestroy$.next();
    this._onDestroy$.complete();
  }

  loadReceipts = this.effect(
    (done$: Observable<(receipts: ReceiptDTO[]) => void | undefined>) =>
      done$.pipe(
        withLatestFrom(this.orderItem$),
        filter(([, orderItem]) => !!orderItem),
        switchMap(([done, orderItem]) =>
          this._domainReceiptService
            .getReceipts({
              receiptType: 65 as ReceiptType,
              ids: [orderItem.orderItemSubsetId],
              eagerLoading: 1,
            })
            .pipe(
              tapResponse(
                (res) => {
                  const receipts = res.result
                    .map((r) => r.item3?.data)
                    .filter((f) => !!f);
                  this.receipts = receipts;

                  if (typeof done === 'function') {
                    done?.(receipts);
                  }
                },
                () => {
                  if (typeof done === 'function') {
                    done?.([]);
                  }
                },
              ),
            ),
        ),
      ),
  );

  async saveSpecialComment() {
    const { orderId, orderItemId, orderItemSubsetId } = this.orderItem;

    try {
      this.specialCommentControl.reset(this.specialCommentControl.value);
      await this._omsService
        .patchComment({
          orderId,
          orderItemId,
          orderItemSubsetId,
          specialComment: this.specialCommentControl.value ?? '',
        })
        .pipe(first())
        .toPromise();

      this.orderItem = {
        ...this.orderItem,
        specialComment: this.specialCommentControl.value ?? '',
      };
      this._store.updateOrderItems([this.orderItem]);
      this.specialCommentChanged.emit();
    } catch (error) {
      console.error(error);
    }
  }

  setMore(more: boolean) {
    this.patchState({ more });
    if (more && this.receipts.length === 0) {
      this.loadReceipts(undefined);
    }
  }

  setSelected(selected: boolean) {
    this.selected = selected;
    this.selectedChange.emit(selected);
    this._cdr.markForCheck();
  }

  orderItemFeature(orderItemListItem: OrderItemListItemDTO) {
    const orderItems = this.order?.items;
    return orderItems?.find(
      (orderItem) => orderItem.data.id === orderItemListItem.orderItemId,
    )?.data?.features?.orderType;
  }

  getOrderItemTrackingData(
    orderItemListItem: OrderItemListItemDTO,
  ): Array<{ trackingProvider: string; trackingNumber: string }> {
    const orderItems = this.order?.items;
    const completeTrackingInformation = orderItems
      ?.find((orderItem) => orderItem.data.id === orderItemListItem.orderItemId)
      ?.data?.subsetItems?.find(
        (subsetItem) => subsetItem.id === orderItemListItem.orderItemSubsetId,
      )?.data?.trackingNumber;

    if (!completeTrackingInformation) {
      return;
    }

    // Beispielnummer: 'DHL: 124124' - Bei mehreren Tracking-Informationen muss noch ein Splitter eingebaut werden, je nach dem welcher Trenner verwendet wird
    const trackingInformationPairs = completeTrackingInformation
      .split(':')
      .map((obj) => obj.trim());
    return this._trackingTransformationHelper(trackingInformationPairs);
  }

  // Macht aus einem String Array ein Array von Objekten mit den keys trackingProvider und trackingNumber
  private _trackingTransformationHelper(
    trackingInformationPairs: string[],
  ): Array<{ trackingProvider: string; trackingNumber: string }> {
    return trackingInformationPairs.reduce(
      (acc, current, index, array) => {
        if (index % 2 === 0) {
          acc.push({
            trackingProvider: current,
            trackingNumber: array[index + 1],
          });
        }
        return acc;
      },
      [] as { trackingProvider: string; trackingNumber: string }[],
    );
  }

  getTrackingNumberLink(trackingNumber: string) {
    return `https://www.dhl.de/de/privatkunden/dhl-sendungsverfolgung.html?piececode=${trackingNumber}`;
  }

  triggerResize() {
    this.autosize.reset();
  }
}
