import {
  Component,
  ChangeDetectionStrategy,
  OnInit,
  OnDestroy,
  ViewChild,
  TrackByFunction,
  ViewChildren,
  QueryList,
  AfterViewInit,
  inject,
  DestroyRef,
} from '@angular/core';
import { debounceTime, filter, first, map, shareReplay, switchMap, takeUntil, withLatestFrom } from 'rxjs/operators';
import { KeyValueDTOOfStringAndString, OrderItemListItemDTO } from '@generated/swagger/oms-api';
import { ActivatedRoute, NavigationStart, Router } from '@angular/router';
import { CustomerOrderSearchStore } from '../customer-order-search.store';
import { BehaviorSubject, combineLatest, Observable, 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';
import { UiFilter } from '@ui/filter';
import { EnvironmentService } from '@core/environment';
import { isEqual } from 'lodash';
import { CustomerOrdersNavigationService } from '@shared/services/navigation';
import { ApplicationService } from '@core/application';
import { Filter, FilterInputGroupMainComponent } from '@shared/components/filter';
import { CustomerOrderItemComponent } from './customer-order-item.component';
import { CacheService } from '@core/cache';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

export interface CustomerOrderSearchResultsState {
  selectedOrderItemSubsetIds: number[];
}

@Component({
  selector: 'page-customer-order-search-results',
  templateUrl: 'customer-order-search-results.component.html',
  styleUrls: ['customer-order-search-results.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  standalone: false,
})
export class CustomerOrderSearchResultsComponent
  extends ComponentStore<CustomerOrderSearchResultsState>
  implements OnInit, AfterViewInit, OnDestroy
{
  @ViewChildren(CustomerOrderItemComponent) listItems: QueryList<CustomerOrderItemComponent>;
  @ViewChild(UiScrollContainerComponent) scrollContainer: UiScrollContainerComponent;

  @ViewChild(FilterInputGroupMainComponent, { static: false })
  sharedFilterInputGroupMain: FilterInputGroupMainComponent;

  destroyRef = inject(DestroyRef);

  items$: Observable<OrderItemListItemDTO[]> = this._customerOrderSearchStore.results$;

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

  hits$ = this._customerOrderSearchStore.hits$;

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

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

  message$ = this._customerOrderSearchStore.message$;

  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._customerOrderSearchStore.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.compartmentInfo ? `${item.compartmentCode}_${item.compartmentInfo}` : item.compartmentCode;

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

  private _onDestroy$ = new Subject<void>();

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

  private _searchResultSubscription = new Subscription();

  filter$ = this._customerOrderSearchStore.filter$.pipe(filter((f) => !!f));

  hasFilter$ = combineLatest([this.filter$, this._customerOrderSearchStore.defaultSettings$]).pipe(
    map(([filter, defaultFilter]) => !isEqual(filter?.getQueryParams(), Filter.create(defaultFilter).getQueryParams())),
  );

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

  get isDesktopLarge() {
    return this._environment.matchDesktopLarge();
  }

  get filterRoute() {
    const compartmentCode = this._activatedRoute?.snapshot?.params?.compartmentCode;
    const orderId = this._activatedRoute?.snapshot?.params?.orderId;
    const processingStatus = this._activatedRoute?.snapshot?.params?.processingStatus;

    return this._navigationService.getCustomerOrdersResultsAndFilterPath({
      processId: this._application.activatedProcessId,
      processingStatus,
      compartmentCode: compartmentCode ?? undefined,
      orderId: Number(orderId),
    }).path;
  }

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

  private readonly SCROLL_POSITION_TOKEN = 'CUSTOMER_ORDERS_LIST_SCROLL_POSITION';

  constructor(
    private _customerOrderSearchStore: CustomerOrderSearchStore,
    private _activatedRoute: ActivatedRoute,
    private _breadcrumb: BreadcrumbService,
    private _commandService: CommandService,
    private _modal: UiModalService,
    private _environment: EnvironmentService,
    private _navigationService: CustomerOrdersNavigationService,
    private _application: ApplicationService,
    private _cache: CacheService,
    private _router: Router,
  ) {
    super({
      selectedOrderItemSubsetIds: [],
    });
  }

  ngOnInit() {
    // Aktualisiere die Result Liste über actionHandled oder specialCommentChanged in den CustomerOrderDetails
    // updateResults wird über actionHandled oder specialCommentChanged in die queryParams überführt
    this._searchResultSubscription.add(
      this._activatedRoute.queryParams.subscribe((queryParams) => {
        const updateResults = queryParams.updateResults;
        if (updateResults) {
          this.search({ silentReload: true });
          const clean = { ...queryParams };
          delete clean['updateResults'];
        }
      }),
    );

    this._searchResultSubscription.add(
      combineLatest([this.processId$, this._activatedRoute.queryParams])
        .pipe(
          debounceTime(150),
          switchMap(([processId, params]) =>
            this._application
              .getSelectedBranch$(processId)
              .pipe(map((selectedBranch) => ({ processId, params, selectedBranch }))),
          ),
        )
        .subscribe(async ({ processId, params, selectedBranch }) => {
          const branchChanged = selectedBranch?.id !== this._customerOrderSearchStore?.selectedBranch?.id;

          const processChanged = processId !== this._customerOrderSearchStore.processId;

          if (processChanged) {
            if (!!this._customerOrderSearchStore.processId && this._customerOrderSearchStore.filter instanceof Filter) {
              await this.updateBreadcrumb(processId, this._customerOrderSearchStore.filter?.getQueryParams());
            }
            this._customerOrderSearchStore.patchState({ processId });
          }

          if (branchChanged) {
            this._customerOrderSearchStore.setBranch(selectedBranch);
          }

          if (!(this._customerOrderSearchStore.filter instanceof UiFilter)) {
            await this._customerOrderSearchStore.loadDefaultSettings();
          }

          const cleanQueryParams = this.cleanupQueryParams(params);

          if (
            !isEqual(cleanQueryParams, this.cleanupQueryParams(this._customerOrderSearchStore.filter.getQueryParams()))
          ) {
            this._customerOrderSearchStore.setQueryParams(params);

            const queryToken = {
              ...this._customerOrderSearchStore.filter.getQueryParams(),
              processId,
              branchId: String(selectedBranch?.id),
            };
            const data = await this._customerOrderSearchStore.getCachedData({ queryToken });

            if (data?.results?.length > 0) {
              this._customerOrderSearchStore.patchState({
                results: data?.results,
                hits: data?.hits,
              });
            }

            if (
              data.results?.length === 0 &&
              this._activatedRoute?.parent?.children?.find((childRoute) => childRoute?.outlet === 'side')?.snapshot
                ?.routeConfig?.path !== 'filter'
            ) {
              this.search({ clear: true });
            }
          } else if (branchChanged) {
            this._customerOrderSearchStore.search({ siletReload: true });
          }

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

          const process = await this._application.getProcessById$(processId).pipe(first()).toPromise();
          if (process) {
            await this.createBreadcrumb(processId, params);
            await this.updateBreadcrumb(processId, params);
          }

          if (this._activatedRoute?.outlet === 'primary') {
            await this.removeDetailsBreadcrumb(processId);
          }
        }),
    );

    this._searchResultSubscription.add(
      this._customerOrderSearchStore.searchResultSubject
        .pipe(withLatestFrom(this.processId$))
        .subscribe(async ([result, processId]) => {
          const queryParams = this._customerOrderSearchStore.filter?.getQueryParams();
          this.createBreadcrumb(processId, queryParams);

          if (result.results.hits === 0) {
            return;
          }

          // Navigation auf Details bzw. Results | Details wenn hits 1
          // Navigation auf Results bei clear search, oder immer auf Tablet oder wenn große Resultliste aktiv ist
          if (result.results.hits === 1) {
            await this.navigateToDetails(
              processId,
              result?.results?.result?.find((_) => true),
              queryParams,
            );
          } else if ((!!result?.clear || this._activatedRoute.outlet === 'primary') && this.isDesktopLarge) {
            await this._navigationService.getCustomerOrdersResultsPath(processId, { queryParams }).navigate();
          }
        }),
    );

    this._customerOrderSearchStore.searchResultClearedSubject
      .pipe(takeUntil(this._onDestroy$))
      .subscribe((_) => this.clearSelectedItems());

    // #4143 To make Splitscreen Search and Filter work combined
    this._searchResultSubscription.add(
      this._customerOrderSearchStore.searchStarted.subscribe(async (_) => {
        const queryParams = {
          ...this.cleanupQueryParams(this._customerOrderSearchStore?.filter?.getQueryParams()),
          main_qs: this.sharedFilterInputGroupMain?.uiInput?.value,
        };
        this._customerOrderSearchStore?.setQueryParams(queryParams);
      }),
    );

    this._router.events.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event) => {
      if (event instanceof NavigationStart) {
        this.cacheResults();
        this._addScrollPositionToCache();
      }
    });
  }

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

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

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

  private async _getScrollPositionFromCache(): Promise<number> {
    return await this._cache.get<number>({
      processId: this._customerOrderSearchStore.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);
  }

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

    this._searchResultSubscription.unsubscribe();
    await this.updateBreadcrumb(
      this._customerOrderSearchStore.processId,
      this._customerOrderSearchStore.filter?.getQueryParams(),
    );
  }

  cleanupQueryParams(params: Record<string, string> = {}) {
    const clean = { ...params };

    for (const key in clean) {
      if (Object.prototype.hasOwnProperty.call(clean, key)) {
        if (clean[key] == undefined) {
          delete clean[key];
        }
      }
    }

    return clean;
  }

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

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

  async createMainBreadcrumb(processId: number, params: Record<string, string>) {
    await this._breadcrumb.addBreadcrumbIfNotExists({
      key: processId,
      name: 'Kundenbestellung',
      path: this._navigationService.getCustomerOrdersBasePath(processId).path,
      params,
      tags: ['customer-order', 'main', 'filter'],
      section: 'customer',
    });
  }

  async createBreadcrumb(processId: number, params: Record<string, string>) {
    await this.createMainBreadcrumb(processId, params);
    await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
      key: processId,
      name: this.getBreadcrumbName(params),
      path: this._navigationService.getCustomerOrdersResultsPath(processId).path,
      section: 'customer',
      params,
      tags: ['customer-order', 'results', 'filter'],
    });
  }

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

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

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

  cacheResults() {
    const queryToken = {
      ...this._customerOrderSearchStore.filter?.getQueryParams(),
      processId: this._customerOrderSearchStore.processId,
      branchId: String(this._customerOrderSearchStore.selectedBranch?.id),
    };

    this._customerOrderSearchStore.setCache({
      queryToken,
      hits: this._customerOrderSearchStore.hits,
      results: this._customerOrderSearchStore.results,
    });
  }

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

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

  async loadMore() {
    if (
      this._customerOrderSearchStore.hits > this._customerOrderSearchStore.results.length &&
      !this._customerOrderSearchStore.fetching &&
      !this._customerOrderSearchStore.silentFetching
    ) {
      this._customerOrderSearchStore.search({ clear: false });
    }
  }

  search({
    filter,
    silentReload = false,
    clear = false,
  }: {
    filter?: Filter;
    silentReload?: boolean;
    clear?: boolean;
  }) {
    if (filter) {
      this.sharedFilterInputGroupMain.cancelAutocomplete();
      this._customerOrderSearchStore.setQueryParams(filter?.getQueryParams());
    }
    this._customerOrderSearchStore.search({ clear, siletReload: silentReload });
  }

  async navigateToDetails(processId: number, orderItem: OrderItemListItemDTO, queryParams: Record<string, string>) {
    await this._navigationService
      .getCustomerOrdersDetailsPath({
        processId,
        processingStatus: orderItem?.processingStatus,
        compartmentCode: orderItem?.compartmentCode ? encodeURIComponent(orderItem.compartmentCode) : undefined,
        orderId: orderItem?.orderId ? orderItem.orderId : undefined,
        extras: {
          queryParams: orderItem?.compartmentCode
            ? {
                buyerNumber: orderItem.buyerNumber,
                ...queryParams,
              }
            : { ...queryParams },
        },
      })
      .navigate();
  }

  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._customerOrderSearchStore.search({ siletReload: true, clear: true });
      this.clearSelectedItems();
      this.loadingFetchedActionButton$.next(false);
    } catch (error) {
      console.error(error);
      this._modal.open({
        content: UiErrorModalComponent,
        data: error,
      });
      this.loadingFetchedActionButton$.next(false);
    }
  }
}
