import {
  Component,
  ChangeDetectionStrategy,
  inject,
  TrackByFunction,
  OnInit,
  AfterViewInit,
  ViewChildren,
  QueryList,
  ViewChild,
  DestroyRef,
} from '@angular/core';
import { PickupShelfDetailsStore, PickupShelfStore } from '../../store';
import { AsyncPipe } from '@angular/common';
import { debounceTime, map } from 'rxjs/operators';
import { PickUpShelfOutNavigationService } from '@shared/services/navigation';
import { ActivatedRoute, NavigationStart, Router, RouterLink } from '@angular/router';
import { IconModule } from '@shared/components/icon';
import { EnvironmentService } from '@core/environment';
import { Filter, FilterModule } from '@shared/components/filter';
import { BehaviorSubject, Observable, combineLatest } from 'rxjs';
import { isEqual } from 'lodash';
import { PickUpShelfListItemComponent } from '../../shared/pickup-shelf-list-item/pickup-shelf-list-item.component';
import {
  DBHOrderItemListItemDTO,
  KeyValueDTOOfStringAndString,
  OrderItemProcessingStatusValue,
} from '@generated/swagger/oms-api';
import { Group, GroupByPipe } from '@shared/pipes/group-by';
import { UiSpinnerModule } from '@ui/spinner';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
import { PickupShelfListItemLoaderComponent } from '../../shared/pickup-shelf-list-item/pickup-shelf-list-item-loader.component';
import { ActionHandlerService } from '../../services/action-handler.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { CacheService } from '@core/cache';
import { ScrollContainerDirective } from '@shared/directives/scroll-container';
import { MatomoModule } from 'ngx-matomo-client';

@Component({
  selector: 'page-pcikup-shelf-out-list',
  templateUrl: 'pickup-shelf-out-list.component.html',
  styleUrls: ['pickup-shelf-out-list.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  host: { class: 'page-pcikup-shelf-out-list' },
  imports: [
    ScrollContainerDirective,
    AsyncPipe,
    RouterLink,
    IconModule,
    FilterModule,
    PickUpShelfListItemComponent,
    GroupByPipe,
    UiSpinnerModule,
    PickupShelfListItemLoaderComponent,
    MatomoModule
],
})
export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
  @ViewChildren(PickUpShelfListItemComponent) listItems: QueryList<PickUpShelfListItemComponent>;
  @ViewChild(ScrollContainerDirective) scrollContainer: ScrollContainerDirective;

  private _pickupShelfOutNavigationService = inject(PickUpShelfOutNavigationService);

  actionHandlerService = inject(ActionHandlerService);

  store = inject(PickupShelfStore);
  detailsStore = inject(PickupShelfDetailsStore);
  destroyRef = inject(DestroyRef);

  list$ = this.store.list$;

  hits$ = this.store.listHits$;

  filterPath$ = this.store.processId$.pipe(
    map((processId) => this._pickupShelfOutNavigationService.filterRoute({ processId })),
  );

  get primaryOutletActive$() {
    return this._environment.matchDesktop$.pipe(map((matches) => matches && this._activatedRoute.outlet === 'primary'));
  }

  filter$ = this.store.filter$;

  hasFilter$ = combineLatest([this.filter$, this.store.defaultFilter$]).pipe(
    map(([filter, defaultFilter]) => {
      const filterQueryParams = filter?.getQueryParams();
      return !isEqual(filterQueryParams, Filter.create(defaultFilter).getQueryParams());
    }),
  );

  fetching$: Observable<boolean> = this.store.fetchingList$;

  searchboxHint$ = this.store.searchboxHint$;

  get isTablet$() {
    return this._environment.matchTablet$;
  }

  get isDesktopSmall$() {
    return this._environment.matchDesktopSmall$;
  }

  get processId$() {
    return this.store.processId$;
  }

  get selectedIds$() {
    return this.store.selectedListItems$.pipe(map((items) => items?.map((item) => item.orderItemSubsetId)));
  }

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

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

  trackByGroupFn: TrackByFunction<Group<string, DBHOrderItemListItemDTO>> = (index, group) => group.group;

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

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

  byCompartmentCodeFn = (item: DBHOrderItemListItemDTO) =>
    item.compartmentInfo ? `${item.compartmentCode}_${item.compartmentInfo}` : item.compartmentCode;

  trackByFn: TrackByFunction<DBHOrderItemListItemDTO> = (index, item) =>
    `${item.orderId}${item.orderItemId}${item.orderItemSubsetId}`;

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

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

  private readonly SCROLL_POSITION_TOKEN = 'SHELF_OUT_LIST_SCROLL_POSITION';

  constructor(
    private _environment: EnvironmentService,
    private _activatedRoute: ActivatedRoute,
    private _uiModal: UiModalService,
    private _pickUpShelfOutNavigation: PickUpShelfOutNavigationService,
    private _cache: CacheService,
    private _router: Router,
  ) {}

  ngOnInit() {
    this.processId$.pipe(takeUntilDestroyed(this.destroyRef), debounceTime(150)).subscribe(async (_) => {
      if (!this.store.list.length) {
        this.store.fetchList();
      }

      const scrollPos = await this._getScrollPositionFromCache();
      if (!!scrollPos && this._activatedRoute.outlet === 'primary') {
        setTimeout(() => {
          this.scrollContainer?.scrollTo(scrollPos);
          this._removeScrollPositionFromCache();
        }, 150);
      }
    });

    // OnDestroy would not trigger if switching between 2 Shelf Out processes
    this._router.events.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event) => {
      if (event instanceof NavigationStart) {
        this._addScrollPositionToCache();
      }
    });
  }

  ngAfterViewInit(): void {
    this.scrollItemIntoView();
  }

  private _removeScrollPositionFromCache(): void {
    this._cache.delete({ processId: this.store.processId, token: this.SCROLL_POSITION_TOKEN });
  }

  private _addScrollPositionToCache(): void {
    if (this._activatedRoute.outlet === 'primary') {
      this._cache.set<number>(
        { processId: this.store.processId, token: this.SCROLL_POSITION_TOKEN },
        this.scrollContainer?.scrollPosition,
      );
    }
  }

  private async _getScrollPositionFromCache(): Promise<number> {
    return await this._cache.get<number>({ processId: this.store.processId, token: this.SCROLL_POSITION_TOKEN });
  }

  // After Navigating to Result Side Outlet
  scrollItemIntoView() {
    setTimeout(() => {
      const getPrimaryRouteParams = this._activatedRoute.parent?.children[0]?.snapshot?.params;
      const item = this.listItems?.find((item) => item.item.orderId === Number(getPrimaryRouteParams?.orderId));
      item?.scrollIntoView();
    }, 150);
  }

  getSelectedItem$(item: DBHOrderItemListItemDTO) {
    return this.store.selectedListItems$.pipe(
      map((selectedListItems) => selectedListItems?.find((i) => i?.orderItemSubsetId === item?.orderItemSubsetId)),
    );
  }

  getIsItemSelectable$(item: DBHOrderItemListItemDTO) {
    return this.store.selectedListItems$.pipe(
      map((selectedListItems) => {
        if (selectedListItems.length > 0) {
          const firstSelectedItem = selectedListItems[0];
          const selectedLabel = firstSelectedItem.actions.find((action) => this.selectionRules(action)).label;
          return item?.actions?.some((action) => this.selectionRules(action) && action.label === selectedLabel);
        } else {
          return item?.actions?.some((action) => this.selectionRules(action));
        }
      }),
    );
  }

  getItemDetailsLink(item: DBHOrderItemListItemDTO) {
    return this._pickUpShelfOutNavigation.detailRoute({
      processId: this.store.processId,
      item: {
        orderId: item?.orderId,
        orderNumber: item?.orderNumber,
        compartmentCode: item?.compartmentCode,
        processingStatus: item?.processingStatus,
        compartmentInfo: item?.compartmentInfo,
      },
    }).path;
  }

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

  search(filter: Filter) {
    this.store.setQueryParams(filter.getQueryParams());
    this.store.fetchList();
  }

  loadMore() {
    this.store.fetchMoreList();
  }

  async handleAction(action: KeyValueDTOOfStringAndString) {
    this.loadingFetchedActionButton$.next(true);
    try {
      const selectedItems = this.store.selectedListItems;
      await this.actionHandlerService.handle({
        action,
        items: selectedItems,
      });
      this.store.fetchList({ emitFetchListResponse: false });
      this.store.resetSelectedListItems();
      this.loadingFetchedActionButton$.next(false);

      const processingStatus = (
        action.key ? +action.key : selectedItems[0].processingStatus
      ) as OrderItemProcessingStatusValue;

      selectedItems.forEach((item) => {
        this.detailsStore.patchOrderItemSubsetInState({
          orderItemSubsetId: item.orderItemSubsetId,
          changes: { processingStatus },
        });
      });
    } catch (error) {
      console.error(error);
      this._uiModal.open({
        content: UiErrorModalComponent,
        data: error,
      });
      this.loadingFetchedActionButton$.next(false);
    }
  }
}
