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

export interface GoodsOutDetailsComponentState {
  fetching: boolean;
  orderNumber?: string;
  processingStatus?: OrderItemProcessingStatusValue;
  compartmentCode?: string;
  items?: OrderItemListItemDTO[];
  orderId?: number;
}

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

  compartmentCode$ = this.select((s) => decodeURIComponent(s.compartmentCode ?? '') || undefined);

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

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

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

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

  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()
  );

  get processId() {
    return +this._activatedRoute.snapshot.parent.data.processId;
  }

  processId$ = this._activatedRoute.parent.data.pipe(map((params) => +params.processId));

  private _onDestroy$ = new Subject();

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

  ngOnInit() {
    this._activatedRoute.params.pipe(takeUntil(this._onDestroy$)).subscribe(async (params) => {
      const orderNumber: string = params?.orderNumber;
      const compartmentCode = params?.compartmentCode;
      const processingStatus = +params?.processingStatus as OrderItemProcessingStatusValue;

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

    this.removeBreadcrumbs();
  }

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

  async updateBreadcrumb(item: OrderItemListItemDTO) {
    if (item) {
      await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
        key: this.processId,
        name: item?.compartmentCode || item?.orderNumber,
        path: this.getDetailsPath(item),
        section: 'customer',
        tags: ['goods-out', 'details', item?.compartmentCode || item?.orderNumber],
      });
    }
  }

  async removeBreadcrumbs() {
    const editCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$(this.processId, ['goods-out', 'edit']).pipe(first()).toPromise();

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

  async removeDetailsCrumbs() {
    const detailsCrumbs = await this._breadcrumb
      .getBreadcrumbsByKeyAndTags$(this.processId, ['goods-out', 'details'])
      .pipe(first())
      .toPromise();

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

  // tslint:disable-next-line: member-ordering
  loadItems = this.effect(($) =>
    $.pipe(
      tap(() => this.patchState({ fetching: false })),
      debounceTime(500),
      withLatestFrom(this.orderNumber$, this.compartmentCode$, this.processingStatus$),
      switchMap(([_, orderNumber, compartmentCode, processingStatus]) => {
        let request$: Observable<ListResponseArgsOfOrderItemListItemDTO>;
        if (compartmentCode) {
          request$ = this._domainGoodsInService.getWarenausgabeItemByCompartment(compartmentCode);
        } else {
          request$ = this._domainGoodsInService.getWarenausgabeItemByOrderNumber(orderNumber);
        }

        return request$.pipe(
          tapResponse(
            (res) => {
              this.patchState({
                items: res.result,
                orderId: res.result[0].orderId,
              });
              this.updateBreadcrumb(res.result.find((item) => item.processingStatus === processingStatus));
            },
            (err) => {},
            () => {
              this.patchState({ fetching: false });
            }
          )
        );
      })
    )
  );

  navigateToEditPage(orderItem: OrderItemListItemDTO) {
    this._router.navigate([this.getEditPath(orderItem)]);
  }

  navigateToDetailsPage(item: OrderItemListItemDTO) {
    this._router.navigate([this.getDetailsPath(item)]);
  }

  navigateToLandingPage() {
    this._router.navigate([`/kunde/${this.processId}/goods/out`]);
  }

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

  getDetailsPath(item: OrderItemListItemDTO) {
    return item?.compartmentCode
      ? `/kunde/${this.processId}/goods/out/details/compartment/${encodeURIComponent(item?.compartmentCode)}/${item?.processingStatus}`
      : `/kunde/${this.processId}/goods/out/details/order/${encodeURIComponent(item?.orderNumber)}/${item?.processingStatus}`;
  }

  getEditPath(item: OrderItemListItemDTO) {
    return item?.compartmentCode
      ? `/kunde/${this.processId}/goods/out/details/compartment/${encodeURIComponent(item?.compartmentCode)}/${item?.processingStatus}/edit`
      : `/kunde/${this.processId}/goods/out/details/order/${encodeURIComponent(item?.orderNumber)}/${item?.processingStatus}/edit`;
  }
}
