import { AfterContentInit, ContentChild, EventEmitter, forwardRef, Input, OnDestroy, Output } from '@angular/core';
import { Component, ChangeDetectionStrategy, ContentChildren, QueryList } from '@angular/core';
import { BreadcrumbService } from '@core/breadcrumb';
import { CommandService } from '@core/command';
import { OrderItemsContext } from '@domain/oms';
import { KeyValueDTOOfStringAndString, OrderItemListItemDTO } from '@swagger/oms';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
import { BehaviorSubject, combineLatest, merge, of, Subscription } from 'rxjs';
import { first, switchMap } from 'rxjs/operators';
import { SharedGoodsInOutOrderDetailsCoversComponent } from './goods-in-out-order-details-covers';
import { SharedGoodsInOutOrderDetailsItemComponent } from './goods-in-out-order-details-item';
import { SharedGoodsInOutOrderDetailsTagsComponent } from './goods-in-out-order-details-tags';
import { SharedGoodsInOutOrderDetailsStore } from './goods-in-out-order-details.store';

@Component({
  selector: 'shared-goods-in-out-order-details',
  templateUrl: 'goods-in-out-order-details.component.html',
  styleUrls: ['goods-in-out-order-details.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [
    {
      provide: SharedGoodsInOutOrderDetailsStore,
      useExisting: forwardRef(() => SharedGoodsInOutOrderDetailsComponent),
    },
  ],
})
export class SharedGoodsInOutOrderDetailsComponent extends SharedGoodsInOutOrderDetailsStore implements AfterContentInit, OnDestroy {
  @ContentChildren(SharedGoodsInOutOrderDetailsItemComponent)
  orderDetailsItemComponents: QueryList<SharedGoodsInOutOrderDetailsItemComponent>;

  @ContentChild(SharedGoodsInOutOrderDetailsCoversComponent, { static: false })
  orderDetailsCovers: SharedGoodsInOutOrderDetailsCoversComponent;

  @ContentChild(SharedGoodsInOutOrderDetailsTagsComponent, { static: false })
  orderDetailsTags: SharedGoodsInOutOrderDetailsTagsComponent;

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

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

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

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

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

  private _subscriptions = new Subscription();

  constructor(private commandService: CommandService, private _breadcrumb: BreadcrumbService, private _modal: UiModalService) {
    super();
  }

  ngAfterContentInit() {
    this._registerOrderDetailsItemComponentsChanges();
    this._registerOrderDetailsTagsComponentsChanges();
    this._registerOrderDetailCoverItemsChanges();
  }

  private _registerOrderDetailsItemComponentsChanges() {
    this._subscriptions.add(
      merge(of(undefined), this.orderDetailsItemComponents.changes)
        .pipe(switchMap((_) => combineLatest(this.orderDetailsItemComponents.map((orderItemComponent) => orderItemComponent.orderItem$))))
        .subscribe((orderItems) => (this.orderItems = orderItems))
    );
  }

  private _registerOrderDetailsTagsComponentsChanges() {
    this._subscriptions.add(
      this.orderItems$.subscribe((_) => {
        this.orderDetailsTags?.writeValue(this.compartmentInfo);
        this.orderDetailsTags?.registerOnChange((compartmentInfo) => (this.compartmentInfo = compartmentInfo));
      })
    );
  }

  private _registerOrderDetailCoverItemsChanges() {
    this._subscriptions.add(
      this.orderDetailsCovers?.orderItems$.subscribe((orderItems) => {
        this.coverItems = orderItems;
      })
    );
  }

  ngOnDestroy() {
    this._subscriptions?.unsubscribe();
  }

  getItemQuantityMap(): Map<number, number> {
    return new Map(
      this.orderDetailsItemComponents.toArray().map((component) => [component.orderItem.orderItemSubsetId, component.quantity])
    );
  }

  getitemsToUpdate(): OrderItemListItemDTO[] {
    if (this.fetchPartial && this.itemsSelectable && this.orderItems.length > 1) {
      return this.orderItems.filter((orderItem) => this.selectedeOrderItemSubsetIds.includes(orderItem.orderItemSubsetId));
    }

    return this.orderItems;
  }

  updateCoverItems(items: OrderItemListItemDTO[]) {
    if (this.orderDetailsCovers?.orderItems) {
      this.orderDetailsCovers.orderItems = this.orderDetailsCovers.orderItems.map((item) => {
        const newItem = items.find((i) => i.orderItemSubsetId === item.orderItemSubsetId);
        return newItem ? newItem : item;
      });
      this.orderDetailsCovers.orderItems = this.orderDetailsCovers.orderItems.filter((item) =>
        [16, 128, 8192, 1048576].includes(item.processingStatus)
      );
    }
  }

  async arrivedActionNavigation() {
    const detailsCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'details']).pipe(first()).toPromise();
    const reservationCrumbs = await this._breadcrumb
      .getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'reservation'])
      .pipe(first())
      .toPromise();

    detailsCrumbs.forEach((crumb) => {
      this._breadcrumb.removeBreadcrumb(crumb.id, true);
    });

    if (reservationCrumbs.length === 0) {
      return 'main';
    } else if (reservationCrumbs.length > 0) {
      return 'reservation';
    }
  }

  async handleAction(
    action: KeyValueDTOOfStringAndString,
    { compartmentCode, compartmentInfo }: { compartmentCode?: string; compartmentInfo?: string } = {}
  ) {
    if (action.command.includes('FETCHED_PARTIAL')) {
      this.patchState({ fetchPartial: true });
      return;
    }

    // #2737 Bei Zubuchen kein Abholfachzettel ausdrucken
    let command = action.command;
    if (compartmentCode) {
      command = action.command.replace('|PRINT_COMPARTMENTLABEL', '');
    }

    this.changeActionLoader$.next(action.command);
    this.changeActionDisabled$.next(true);

    let commandData: OrderItemsContext = {
      items: this.getitemsToUpdate(),
      compartmentInfo: compartmentInfo || this.compartmentInfo,
      compartmentCode:
        action.command.includes('PRINT_PRICEDIFFQRCODELABEL') && !compartmentCode ? this.orderItems[0]?.compartmentCode : compartmentCode,
      itemQuantity: this.getItemQuantityMap(),
      receipts: this.receipts,
    };
    try {
      commandData = await this.commandService.handleCommand(command, commandData);
      let navigateTo: 'details' | 'main' | 'reservation' = 'details';
      if (action.command.includes('ARRIVED')) {
        navigateTo = await this.arrivedActionNavigation();
      }
      if (action.command.includes('PRINT_PRICEDIFFQRCODELABEL')) {
        navigateTo = 'main';
      }

      this.actionHandled.emit({ orderItemsContext: commandData, command: action.command, navigation: navigateTo });
      this.updateOrderItems(commandData.items);
      this.updateCoverItems(commandData.items);
    } catch (error) {
      this._modal.open({
        content: UiErrorModalComponent,
        data: error,
      });
      console.error(error);
    }

    this.patchState({ fetchPartial: false });
    this.selectedeOrderItemSubsetIds = [];

    setTimeout(() => {
      this.changeActionDisabled$.next(false);
      this.changeActionLoader$.next(undefined);
    }, 1000);
  }
}
