import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { debounceTime, first, map, shareReplay, takeUntil, tap, withLatestFrom } from 'rxjs/operators';
import { KeyValueDTOOfStringAndString, OrderItemListItemDTO } from '@swagger/oms';
import { ActivatedRoute, Router } from '@angular/router';
import { GoodsOutSearchStore } from '../goods-out-search.store';
import { BehaviorSubject, combineLatest, Subject, Subscription } from 'rxjs';
import { BreadcrumbService } from '@core/breadcrumb';
import { ComponentStore } from '@ngrx/component-store';
import { CommandService } from '@core/command';
import { OrderItemsContext } from '@domain/oms';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
import { UiScrollContainerComponent } from '@ui/scroll-container';

export interface GoodsOutSearchResultsState {
  selectedOrderItemSubsetIds: number[];
}

@Component({
  selector: 'page-goods-out-search-results',
  templateUrl: 'goods-out-search-results.component.html',
  styleUrls: ['goods-out-search-results.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class GoodsOutSearchResultsComponent extends ComponentStore<GoodsOutSearchResultsState> implements OnInit, OnDestroy {
  @ViewChild(UiScrollContainerComponent) scrollContainer: UiScrollContainerComponent;

  items$ = this._goodsOutSearchStore.results$;

  itemLength$ = this.items$.pipe(map((items) => items?.length));

  hits$ = this._goodsOutSearchStore.hits$;

  loading$ = this._goodsOutSearchStore.fetching$.pipe(shareReplay());
  loadingFetchedActionButton$ = new BehaviorSubject<boolean>(false);

  listEmpty$ = combineLatest([this.loading$, this.hits$]).pipe(
    map(([loading, hits]) => !loading && hits === 0),
    shareReplay()
  );

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

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

  actions$ = combineLatest([this.items$, this.selectedOrderItemSubsetIds$]).pipe(
    map(([items, selectedOrderItemSubsetIds]) =>
      items?.find((item) => selectedOrderItemSubsetIds.find((orderItemSubsetId) => item.orderItemSubsetId === orderItemSubsetId))
    ),
    map((item) => item?.actions?.filter((action) => this.selectionRules(action)))
  );

  get selectedItems() {
    const items = this._goodsOutSearchStore.results;
    const selectedOrderItemSubsetIds = this.selectedOrderItemSubsetIds;

    return items.filter((item) => selectedOrderItemSubsetIds.some((orderItemSubsetId) => item.orderItemSubsetId === orderItemSubsetId));
  }

  byBuyerNumberFn = (item: OrderItemListItemDTO) => item.buyerNumber;

  byOrderNumberFn = (item: OrderItemListItemDTO) => item.orderNumber;

  byProcessingStatusFn = (item: OrderItemListItemDTO) => item.processingStatus;

  byCompartmentCodeFn = (item: OrderItemListItemDTO) => item.compartmentCode;

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

  previousProcessId: undefined | number;

  private _onDestroy$ = new Subject();

  trackByFn = (item: OrderItemListItemDTO) => `${item.orderId}${item.orderItemId}${item.orderItemSubsetId}`;

  private _searchResultSubscription: Subscription;

  scrollTo: number;

  constructor(
    private _goodsOutSearchStore: GoodsOutSearchStore,
    private _router: Router,
    private _activatedRoute: ActivatedRoute,
    private _breadcrumb: BreadcrumbService,
    private _commandService: CommandService,
    private _modal: UiModalService
  ) {
    super({
      selectedOrderItemSubsetIds: [],
    });
  }

  saveScrollPosition(processId: number, scrollPosition: number) {
    localStorage.setItem(`SCROLL_POSITION_${processId}`, JSON.stringify(scrollPosition));
  }

  getScrollPosition(processId: number): number | undefined {
    const scroll_position = localStorage.getItem(`SCROLL_POSITION_${processId}`);
    return scroll_position ? JSON.parse(scroll_position) : undefined;
  }

  removeScrollPosition(processId: number) {
    localStorage.removeItem(`SCROLL_POSITION_${processId}`);
  }

  ngOnInit() {
    this.processId$
      .pipe(takeUntil(this._onDestroy$), debounceTime(10), withLatestFrom(this._activatedRoute.queryParams))
      .subscribe(([processId, params]) => {
        if (this.previousProcessId && this.previousProcessId !== processId) {
          this.saveScrollPosition(this.previousProcessId, this.scrollContainer.scrollPos);
        }

        this._goodsOutSearchStore.setQueryParams(params);
        this.updateBreadcrumb(processId, params);

        if (this.previousProcessId !== processId) {
          this.initInitialSearch(processId, params);
          this.createBreadcrumb(processId, params);
          this.removeBreadcrumbs(processId);

          this.previousProcessId = processId;

          this._goodsOutSearchStore.search({ siletReload: true });
        }
      });

    this._goodsOutSearchStore.searchResultCleared.pipe(takeUntil(this._onDestroy$)).subscribe((_) => this.clearSelectedItems());
  }

  ngOnDestroy() {
    this._onDestroy$.next();
    this._onDestroy$.complete();

    if (this._searchResultSubscription) {
      this._searchResultSubscription.unsubscribe();
    }

    this.updateBreadcrumb(this.previousProcessId, this._goodsOutSearchStore.filter?.getQueryParams());
    this.saveScrollPosition(this.previousProcessId, this.scrollContainer.scrollPos);
  }

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

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

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

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

  async createBreadcrumb(processId: number, params: Record<string, string>) {
    await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
      key: processId,
      name: this.getBreadcrumbName(params),
      path: `/kunde/${processId}/goods/out/results`,
      section: 'customer',
      params,
      tags: ['goods-out', 'results', 'filter'],
    });
  }

  async updateBreadcrumb(processId: number, queryParams: Record<string, string>) {
    if (queryParams) {
      const crumbs = await this._breadcrumb
        .getBreadcrumbsByKeyAndTags$(processId, ['goods-out', 'results', 'filter'])
        .pipe(first())
        .toPromise();

      const name = queryParams.main_qs ? queryParams.main_qs : 'Alle Artikel';
      const params = { ...queryParams };

      for (const crumb of crumbs) {
        this._breadcrumb.patchBreadcrumb(crumb.id, {
          name,
          params,
        });
      }
    }
  }

  getBreadcrumbName(params: Record<string, string>) {
    const input = params?.main_qs;

    return input?.replace('ORD:', '') ?? 'Alle';
  }

  initInitialSearch(processId: number, params: Record<string, string>) {
    if (this._searchResultSubscription) {
      this._searchResultSubscription.unsubscribe();
    }

    this._searchResultSubscription = new Subscription();

    this._searchResultSubscription.add(
      this._goodsOutSearchStore.searchResult$.subscribe(async (result) => {
        if (result.hits === 0) {
          await this._router.navigate([`/kunde/${processId}/goods/out`], {
            queryParams: this._goodsOutSearchStore.filter.getQueryParams(),
          });
        } else {
          await this.createBreadcrumb(processId, params);
          if (result.hits === 1) {
            await this.navigateToDetails(processId, result.result[0]);
          } else {
            const scrollPos = this.getScrollPosition(processId);
            if (scrollPos) {
              this.scrollTo = scrollPos;
              this.scrollContainer?.scrollTo(scrollPos);
              this.removeScrollPosition(processId);
            }
          }
        }
      })
    );

    this._searchResultSubscription.add(
      this._goodsOutSearchStore.searchResultFromCache$.pipe(takeUntil(this._onDestroy$)).subscribe(async (result) => {
        if (result?.hits > 0) {
          const scrollPos = this.getScrollPosition(processId);
          if (scrollPos) {
            this.scrollTo = scrollPos;
            this.scrollContainer?.scrollTo(scrollPos);
            this.removeScrollPosition(processId);
          }
        }
      })
    );
  }

  async loadMore() {
    if (this._goodsOutSearchStore.hits > this._goodsOutSearchStore.results.length && !this._goodsOutSearchStore.fetching) {
      this.saveScrollPosition(this.previousProcessId, this.scrollContainer.scrollPos);
      this._goodsOutSearchStore.search({});
    }
  }

  navigateToDetails(processId: number, orderItem: OrderItemListItemDTO) {
    const orderNumber = orderItem.orderNumber;
    const processingStatus = orderItem.processingStatus;
    const compartmentCode = orderItem.compartmentCode;

    if (compartmentCode) {
      this._router.navigate([
        `/kunde/${processId}/goods/out/details/compartment/${encodeURIComponent(compartmentCode)}/${processingStatus}`,
      ]);
    } else {
      this._router.navigate([`/kunde/${processId}/goods/out/details/order/${encodeURIComponent(orderNumber)}/${processingStatus}`]);
    }
  }

  setSelectedItem(item: OrderItemListItemDTO, selected: boolean) {
    const included = this.selectedOrderItemSubsetIds.includes(item.orderItemSubsetId);

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

  clearSelectedItems() {
    this.patchState({ selectedOrderItemSubsetIds: [] });
  }

  selectionRules(action: KeyValueDTOOfStringAndString) {
    return (
      (action.command.includes('FETCHED') && action.key === '256' && action.enabled !== false) ||
      action.command.includes('COLLECT_ON_DELIVERYNOTE')
    );
  }

  async handleAction(action: KeyValueDTOOfStringAndString) {
    this.loadingFetchedActionButton$.next(true);
    const commandData: OrderItemsContext = {
      items: this.selectedItems,
    };
    try {
      await this._commandService.handleCommand(action.command, commandData);
      this._goodsOutSearchStore.search({ siletReload: true });
      this.clearSelectedItems();
      this.loadingFetchedActionButton$.next(false);
    } catch (error) {
      console.error(error);
      this._modal.open({
        content: UiErrorModalComponent,
        data: error,
      });
      this.loadingFetchedActionButton$.next(false);
    }
  }
}
