import { Injectable, OnDestroy } from '@angular/core';
import { DomainCustomerOrderService, DomainOmsService, OrderItemsContext } from '@domain/oms';
import { ComponentStore } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';

import { OrderDTO, OrderItemListItemDTO, OrderItemProcessingStatusValue, ReceiptDTO } from '@generated/swagger/oms-api';
import { log } from '@utils/common';
import { isEqual } from 'lodash';
import { BehaviorSubject, combineLatest, Subject } from 'rxjs';
import { debounceTime, map, switchMap, tap, withLatestFrom } from 'rxjs/operators';

export interface CustomerOrderDetailsComponentState {
  order?: OrderDTO;
  orderId?: number;
  buyerNumber?: string;
  items?: OrderItemListItemDTO[];
  fetching?: boolean;
  processingStatus?: OrderItemProcessingStatusValue;
  receipts: ReceiptDTO[];
  compartmentCode?: string;
  compartmentInfo?: string;
  latestCompartmentCode?: string;
  latestCompartmentInfo?: string;
  fetchPartial?: boolean;
  itemsSelectable?: boolean;
  selectedeOrderItemSubsetIds: number[];
}

@Injectable()
export class CustomerOrderDetailsStore extends ComponentStore<CustomerOrderDetailsComponentState> implements OnDestroy {
  get items() {
    return this.get((s) => s.items);
  }
  set items(items: OrderItemListItemDTO[]) {
    if (!isEqual(this.items, items)) {
      this.patchState({ items });
    }
  }

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

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

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

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

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

  get processingStatus() {
    return this.get((s) => s.processingStatus);
  }
  set processingStatus(processingStatus: OrderItemProcessingStatusValue) {
    this.patchState({ processingStatus });
  }

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

  get itemsSelectable() {
    return this.get((s) => s.itemsSelectable);
  }
  set itemsSelectable(itemsSelectable: boolean) {
    this.patchState({ itemsSelectable });
  }

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

  get fetching() {
    return this.get((s) => s.fetching);
  }
  set fetching(fetching: boolean) {
    this.patchState({ fetching });
  }

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

  get compartmentCode() {
    return this.get((s) => s.compartmentCode);
  }
  set compartmentCode(compartmentCode: string) {
    this.patchState({ compartmentCode });
  }

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

  get buyerNumber() {
    return this.get((s) => s.buyerNumber);
  }
  set buyerNumber(buyerNumber: string) {
    this.patchState({ buyerNumber });
  }

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

  get selectedeOrderItemSubsetIds() {
    return this.get((s) => s.selectedeOrderItemSubsetIds);
  }
  set selectedeOrderItemSubsetIds(selectedeOrderItemSubsetIds: number[]) {
    if (!isEqual(this.selectedeOrderItemSubsetIds, selectedeOrderItemSubsetIds)) {
      this.patchState({ selectedeOrderItemSubsetIds });
    }
  }

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

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

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

  get fetchPartial() {
    return this.get((s) => s.fetchPartial);
  }

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

  mainActions$ = combineLatest([this.items$, this.fetchPartial$]).pipe(
    map(([items, fetchPartial]) =>
      items
        ?.find((_) => true)
        ?.actions?.filter((action) => typeof action?.enabled !== 'boolean')
        ?.filter((action) => (fetchPartial ? !action.command.includes('FETCHED_PARTIAL') : true))
        ?.sort((a, b) => (a.selected === b.selected ? 0 : a.selected ? -1 : 1)),
    ),
  );

  showTagsComponent$ = this.items$.pipe(
    map((orderItems) => {
      const first = orderItems?.find((_) => true);
      return first?.actions?.some((a) => a.command?.includes('ARRIVED')) || false;
    }),
  );

  get latestCompartmentCode() {
    return this.get((s) => s.latestCompartmentCode);
  }

  set latestCompartmentCode(latestCompartmentCode: string) {
    if (this.latestCompartmentCode !== latestCompartmentCode) {
      this.patchState({ latestCompartmentCode });
    }
  }

  readonly latestCompartmentCode$ = combineLatest([
    this.select((s) => s.latestCompartmentCode),
    this.select((s) => s.latestCompartmentInfo),
  ]).pipe(
    map(([code, info]) => {
      if (info) {
        return `${code}_${info}`;
      }
      return code;
    }),
  );

  get latestCompartmentInfo() {
    return this.get((s) => s.latestCompartmentInfo);
  }

  set latestCompartmentInfo(latestCompartmentInfo: string) {
    if (this.latestCompartmentInfo !== latestCompartmentInfo) {
      this.patchState({ latestCompartmentInfo });
    }
  }

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

  addToPreviousCompartmentAction$ = combineLatest([this.items$, this.latestCompartmentCode$]).pipe(
    map(([orderItems, latestCompartmentCode]) => {
      const orderItem = orderItems?.find((_) => true);

      if ([16, 8192].includes(orderItem?.processingStatus) && latestCompartmentCode) {
        // Zubuchen von Bezahlte und unbezahlte Bestellungen nicht möglich
        // Zubuchen bei Pay&Collect nur innerhalb der gleichen Bestellung möglich

        return orderItem.actions.find((a) => a.key === '128');
      }

      return undefined;
    }),
  );

  searchCompleted = new Subject<CustomerOrderDetailsComponentState>();

  actionHandled = new Subject<{
    orderItemsContext: OrderItemsContext;
    command: string;
    navigation: 'details' | 'main' | 'reservation';
  }>();

  changeActionDisabled$ = new BehaviorSubject<boolean>(false);

  constructor(
    private _domainCustomerOrderService: DomainCustomerOrderService,
    private _domainOmsService: DomainOmsService,
  ) {
    super({
      items: [],
      selectedeOrderItemSubsetIds: [],
      receipts: [],
      fetchPartial: true,
    });
  }

  loadItems = this.effect(($) =>
    $.pipe(
      tap(() => this.patchState({ fetching: true })),
      debounceTime(500),
      withLatestFrom(this.orderId$, this.compartmentCode$),
      switchMap(([_, orderId, compartmentCode]) => {
        return this._domainCustomerOrderService
          .getOrderItemsByOrderNumber({
            compartmentCode,
            orderId,
          })
          .pipe(
            withLatestFrom(this.processingStatus$, this.buyerNumber$),
            tapResponse(
              ([res, processingStatus, buyerNumber]) => {
                const items = res?.result?.filter((item) => {
                  return (
                    item.processingStatus === processingStatus &&
                    (buyerNumber ? item.buyerNumber === buyerNumber : true)
                  );
                });
                this.patchState({
                  items,
                  fetching: false,
                  orderId: items?.find((_) => true)?.orderId,
                });
                this.searchCompleted.next(this.get());
              },
              (err) => {
                console.error(err);
                this.patchState({ fetching: false, items: [] });
                this.searchCompleted.next(this.get());
              },
            ),
          );
      }),
    ),
  );

  loadOrder = this.effect(($) =>
    $.pipe(
      withLatestFrom(this.orderId$),
      switchMap(([_, orderId]) => {
        return this._domainOmsService.getOrder(orderId).pipe(
          tapResponse(
            (response) => {
              this.patchState({ order: response });
            },
            (err) => {
              this.patchState({ order: undefined });
            },
          ),
        );
      }),
    ),
  );

  selectAllOrderItems() {
    this.patchState({ selectedeOrderItemSubsetIds: this.items?.map((item) => item.orderItemSubsetId) });
  }

  selectOrderItem(item: OrderItemListItemDTO, selected: boolean) {
    const included = this.selectedeOrderItemSubsetIds.includes(item?.orderItemSubsetId);

    if (!included && selected) {
      this.patchState({ selectedeOrderItemSubsetIds: [...this.selectedeOrderItemSubsetIds, item.orderItemSubsetId] });
    } else if (included && !selected) {
      this.patchState({
        selectedeOrderItemSubsetIds: this.selectedeOrderItemSubsetIds.filter((id) => id !== item?.orderItemSubsetId),
      });
    }
  }

  updateOrderItems(orderItems: OrderItemListItemDTO[]) {
    this.patchState({
      items: this.items.map((item) => {
        const newItem = orderItems.find((i) => i.orderItemSubsetId === item.orderItemSubsetId);
        return newItem ? newItem : item;
      }),
    });
  }

  updateReceipts(receipts: ReceiptDTO[]) {
    this.patchState({
      receipts,
    });
  }
}
