import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { BreadcrumbService } from '@core/breadcrumb';
import { Config } from '@core/config';
import { DomainGoodsService, DomainOmsService, OrderItemsContext } from '@domain/oms';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { OrderItemListItemDTO, OrderItemProcessingStatusValue } from '@swagger/oms';
import { combineLatest, Subject } from 'rxjs';
import { debounceTime, filter, first, map, shareReplay, switchMap, takeUntil, tap, withLatestFrom } from 'rxjs/operators';

export interface GoodsInDetailsComponentState {
  fetching: boolean;
  fetchingCoverItems: boolean;
  customerNumber?: string;
  orderNumber?: string;
  processingStatus?: OrderItemProcessingStatusValue;
  selectedOrderItemId?: number;
  items?: OrderItemListItemDTO[];
  coverItems?: OrderItemListItemDTO[];
  orderId?: number;
}

@Component({
  selector: 'page-goods-in-details',
  templateUrl: 'goods-in-details.component.html',
  styleUrls: ['goods-in-details.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class GoodsInDetailsComponent extends ComponentStore<GoodsInDetailsComponentState> implements OnInit, OnDestroy {
  customerNumber$ = this.select((s) => decodeURIComponent(s.customerNumber ?? ''));
  orderNumber$ = this.select((s) => decodeURIComponent(s.orderNumber ?? ''));

  selectedOrderItemId$ = this.select((s) => +s.selectedOrderItemId);

  items$ = this.select((s) => s.items ?? []);
  coverItems$ = this.select((s) => s.coverItems ?? []);

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

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

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

  itemsWithProcessingStatus$ = combineLatest([this.items$, this.processingStatus$]).pipe(
    map(([items, processingStatus]) => items.filter((item) => item.processingStatus === processingStatus))
  );

  order$ = this.orderId$.pipe(
    filter((orderId) => !!orderId),
    switchMap((orderId) => this._omsService.getOrder(orderId)),
    shareReplay()
  );

  selectedItem$ = combineLatest([this.selectedOrderItemId$, this.itemsWithProcessingStatus$]).pipe(
    map(([orderItemId, items]) => items.find((item) => item.orderItemSubsetId === orderItemId) || items[0])
  );

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

  private _onDestroy$ = new Subject();

  constructor(
    private _activatedRoute: ActivatedRoute,
    private _domainGoodsInService: DomainGoodsService,
    private _omsService: DomainOmsService,
    private _breadcrumb: BreadcrumbService,
    private _router: Router,
    private readonly _config: Config
  ) {
    super({
      fetching: false,
      fetchingCoverItems: false,
    });
  }

  ngOnInit() {
    this._activatedRoute.params.pipe(takeUntil(this._onDestroy$)).subscribe(async (params) => {
      const customerNumber: string = params?.customerNumber;
      const orderNumber: string = params?.orderNumber;
      const selectedOrderItemId: number = +params?.orderItemId || undefined;
      const processingStatus = +params?.processingStatus as OrderItemProcessingStatusValue;

      this.patchState({ customerNumber, orderNumber, selectedOrderItemId, processingStatus });
      await this.removeDetailsCrumbs();
      this.loadItems();
    });

    this.removeBreadcrumbs();
    this.loadCoverItems();
  }

  ngOnDestroy() {
    this._onDestroy$.next();
    this._onDestroy$.unsubscribe();
  }

  async removeBreadcrumbs() {
    const editCrumbs = await this._breadcrumb
      .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'edit'])
      .pipe(first())
      .toPromise();

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

  async removeDetailsCrumbs() {
    const detailsCrumbs = await this._breadcrumb
      .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'details'])
      .pipe(first())
      .toPromise();

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

  async updateBreadcrumb(item: OrderItemListItemDTO) {
    if (item) {
      await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
        key: this._config.get('process.ids.goodsIn'),
        name: item?.orderNumber,
        path: `/filiale/goods/in/details/customer/${encodeURIComponent(item.buyerNumber)}/order/${encodeURIComponent(
          item?.orderNumber
        )}/item/${item?.orderItemSubsetId}/${item?.processingStatus}`,
        section: 'branch',
        tags: ['goods-in', 'details', item?.orderNumber],
      });
    }
  }

  // tslint:disable-next-line: member-ordering
  loadItems = this.effect(($) =>
    $.pipe(
      tap(() => this.patchState({ fetching: true })),
      debounceTime(500),
      withLatestFrom(this.orderNumber$, this.selectedOrderItemId$, this.processingStatus$),
      switchMap(([_, orderNumber, selectedOrderItemId, processingStatus]) =>
        this._domainGoodsInService.getWareneingangItemByOrderNumber(orderNumber).pipe(
          tapResponse(
            (res) => {
              this.patchState({
                items: res.result.map((item) => ({
                  ...item,
                  // Anzeige von Teilabholung in Wareneingang ausblenden
                  actions: item?.actions?.filter((action) => action.command !== 'FETCHED_PARTIAL') ?? [],
                })),
                orderId: res.result[0].orderId,
              });
              this.updateBreadcrumb(
                res.result.find((item) => item.orderItemSubsetId === selectedOrderItemId && item.processingStatus === processingStatus)
              );
            },
            () => {},
            () => {
              this.patchState({ fetching: false });
            }
          )
        )
      )
    )
  );

  loadCoverItems = this.effect(($) =>
    $.pipe(
      tap(() => this.patchState({ fetchingCoverItems: true })),
      debounceTime(200),
      withLatestFrom(this.customerNumber$),
      switchMap(([_, customerNumber]) =>
        this._domainGoodsInService.getWareneingangItemByCustomerNumber(customerNumber).pipe(
          tapResponse(
            (res) => {
              this.patchState({
                coverItems: res?.result?.map((item) => ({
                  ...item,
                })),
                fetchingCoverItems: false,
              });
            },
            () => {},
            () => {
              this.patchState({ fetchingCoverItems: false });
            }
          )
        )
      )
    )
  );

  navigateToLandingPage() {
    this._router.navigate(['/filiale/goods/in/']);
  }

  navigateToReservationPage() {
    this._router.navigate(['/filiale/goods/in/reservation']);
  }

  navigateToEditPage(orderItem: OrderItemListItemDTO) {
    this._router.navigate([
      `/filiale/goods/in/details/customer/${encodeURIComponent(orderItem.buyerNumber)}/order/${encodeURIComponent(
        orderItem?.orderNumber
      )}/item/${orderItem?.orderItemSubsetId}/${orderItem?.processingStatus}/edit`,
    ]);
  }

  navigateToDetailsPage(orderItem: OrderItemListItemDTO) {
    this._router.navigate([
      `/filiale/goods/in/details/customer/${encodeURIComponent(orderItem?.buyerNumber)}/order/${encodeURIComponent(
        orderItem?.orderNumber
      )}/item/${orderItem?.orderItemSubsetId}/${orderItem?.processingStatus}`,
    ]);
  }

  coverClick(orderItems: OrderItemListItemDTO[]) {
    if (orderItems.length === 1) {
      this.navigateToDetailsPage(orderItems[0]);
    } else {
      if (orderItems.every((item) => item.processingStatus === 128)) {
        // Search by processingStatus and compartmentCode
        const queryParams = {
          main_qs: encodeURIComponent(orderItems.find((_) => true).compartmentCode),
          filter_orderitemprocessingstatus: orderItems.find((_) => true).processingStatus,
        };
        this._router.navigate(['/filiale/goods/in', 'results'], { queryParams });
      } else {
        // Search by processingStatus and orderNumber
        const queryParams = {
          main_qs: encodeURIComponent(orderItems.find((_) => true).orderNumber),
          filter_orderitemprocessingstatus: orderItems
            .map((item) => item.processingStatus)
            .filter((v, i, a) => a.indexOf(v) === i)
            .join(';'),
        };
        this._router.navigate(['/filiale/goods/in', 'results'], { queryParams });
      }
    }
  }

  async actionHandled(handler: { orderItemsContext: OrderItemsContext; command: string; navigation: 'details' | 'main' | 'reservation' }) {
    switch (handler.navigation) {
      case 'details':
        this.navigateToDetailsPage(handler.orderItemsContext.items.find((_) => true));
        await this.removeDetailsCrumbs();
        this.loadItems();
        break;
      case 'main':
        this.navigateToLandingPage();
        break;
      case 'reservation':
        this.navigateToReservationPage();
        break;
      default:
        break;
    }
  }
}
