import {
  ChangeDetectionStrategy,
  Component,
  OnDestroy,
  OnInit,
  QueryList,
  ViewChild,
  ViewChildren,
  inject,
} from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { BreadcrumbService } from '@core/breadcrumb';
import { CommandService } from '@core/command';
import { Config } from '@core/config';
import { OrderItemsContext } from '@domain/oms';
import { GoodsInOutOrderGroupItemComponent } from '@shared/components/goods-in-out';
import { KeyValueDTOOfStringAndString, OrderItemListItemDTO } from '@generated/swagger/oms-api';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
import { UiScrollContainerComponent } from '@ui/scroll-container';
import { BehaviorSubject, combineLatest, Subject } from 'rxjs';
import { map, shareReplay, takeUntil, first } from 'rxjs/operators';
import { GoodsInReservationStore } from './goods-in-reservation.store';
import { PickupShelfInNavigationService } from '@shared/services/navigation';

@Component({
  selector: 'page-goods-in-reservation',
  templateUrl: 'goods-in-reservation.component.html',
  styleUrls: ['goods-in-reservation.component.scss'],
  providers: [GoodsInReservationStore],
  changeDetection: ChangeDetectionStrategy.OnPush,
  standalone: false,
})
export class GoodsInReservationComponent implements OnInit, OnDestroy {
  private _pickupShelfInNavigationService = inject(PickupShelfInNavigationService);

  @ViewChild(UiScrollContainerComponent) scrollContainer: UiScrollContainerComponent;
  @ViewChildren(GoodsInOutOrderGroupItemComponent) listItems: QueryList<GoodsInOutOrderGroupItemComponent>;

  items$ = this._store.results$;

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

  hits$ = this._store.hits$;

  loading$ = this._store.loading$.pipe(shareReplay());

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

  takeAwayAvailabilities$ = this._store.takeAwayAvailabilities$.pipe(shareReplay());

  selectedOrderItemSubsetIds$ = this._store.selectedOrderItemSubsetIds$;

  get selectedOrderItemSubsetIds() {
    return this._store.selectedOrderItemSubsetIds;
  }

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

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

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

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

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

  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;

  constructor(
    private _breadcrumb: BreadcrumbService,
    private _store: GoodsInReservationStore,
    private _commandService: CommandService,
    private _route: ActivatedRoute,
    private _router: Router,
    private _modal: UiModalService,
    private readonly _config: Config,
  ) {}

  ngOnInit() {
    this.initInitialSearch();
    this.createBreadcrumb();
    this.removeBreadcrumbs();
  }

  ngOnDestroy(): void {
    this._onDestroy$.next();
    this._onDestroy$.complete();
    this.updateBreadcrumb();
  }

  async createBreadcrumb() {
    await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
      key: this._config.get('process.ids.goodsIn'),
      name: 'Reservierungen',
      path: '/filiale/goods/in/reservation',
      section: 'branch',
      params: { view: 'reservation' },
      tags: ['goods-in', 'reservation'],
    });
  }

  async updateBreadcrumb() {
    const crumbs = await this._breadcrumb
      .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'reservation'])
      .pipe(first())
      .toPromise();
    for (const crumb of crumbs) {
      this._breadcrumb.patchBreadcrumb(crumb.id, {
        name: crumb.name,
        params: { scroll_position: this.scrollContainer?.scrollPos ?? 0, take: this._store.results?.length },
      });
    }
  }

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

    breadcrumbsToDelete = breadcrumbsToDelete.filter(
      (crumb) => !crumb.tags.includes('reservation') && !crumb.tags.includes('main'),
    );

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

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

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

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

  navigateToDetails(orderItem: OrderItemListItemDTO) {
    const nav = this._pickupShelfInNavigationService.detailRoute({ item: orderItem, side: false });

    this._router.navigate(nav.path, { queryParams: { ...nav.queryParams, view: 'reservation' } });
  }

  initInitialSearch() {
    if (this._store.hits === 0) {
      this._store.searchResult$.pipe(takeUntil(this._onDestroy$)).subscribe(async (result) => {
        await this.createBreadcrumb();
        if (this._store.searchOptions?.take) {
          this._store.searchOptions = undefined;
          this.scrollContainer.scrollTo(Number(scroll_position ?? 0));
        }
      });
    }

    const { scroll_position, take } = this._route.snapshot.queryParams;
    if (!!take && !!scroll_position) {
      this._store.searchOptions = { take: Number(take) };
    }
    this._store.search();
  }

  async loadMore() {
    if (this._store.hits > this._store.results.length && !this._store.loading) {
      this._store.search();
    }
  }

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

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

  unselectAll() {
    this.listItems.forEach((listItem) => listItem.setSelected(false));
    this._store.patchState({ selectedOrderItemSubsetIds: [] });
  }

  selectionRules(action: KeyValueDTOOfStringAndString) {
    return action.enabled !== false && action.selected === true;
  }

  async handleAction(action: KeyValueDTOOfStringAndString) {
    this.changeActionLoader$.next(true);

    const items = await this.items$.pipe(first()).toPromise();
    const selectedSubsetIds = await this.selectedOrderItemSubsetIds$.pipe(first()).toPromise();
    const itemsToUpdate = items.filter((item) => selectedSubsetIds.includes(item.orderItemSubsetId));
    const buyerNumbers = Array.from(new Set(itemsToUpdate.map((item) => item.buyerNumber)));

    try {
      for (const buyerNumber of buyerNumbers) {
        const itemsToUpdateForEachCustomer = itemsToUpdate.filter((item) => item.buyerNumber === buyerNumber);

        const itemsToUpdateGroupedByCommand = itemsToUpdateForEachCustomer.reduce<
          Record<string, OrderItemListItemDTO[]>
        >((acc, item) => {
          const command = item.actions.find((a) => a.value === action.value)?.command;
          if (!acc[command]) {
            acc[command] = [];
          }
          acc[command].push(item);
          return acc;
        }, {});

        let compartmentCode: string;

        for (const command of Object.keys(itemsToUpdateGroupedByCommand)) {
          const itemsToUpdateForCommand = itemsToUpdateGroupedByCommand[command];
          const itemQuantity = new Map(itemsToUpdateForCommand.map((item) => [item.orderItemSubsetId, item.quantity]));

          const data: OrderItemsContext = {
            items: itemsToUpdateForCommand,
            shippingDelayComment: `Aktion: ${action.label}`,
            itemQuantity,
            compartmentCode,
          };

          const result = await this._commandService.handleCommand(command, data);
          compartmentCode = result.compartmentCode;
        }
      }
      this._store.reload();
      this.unselectAll();
    } catch (error) {
      this._modal.open({
        content: UiErrorModalComponent,
        data: error,
      });
      console.error(error);
    }

    this.changeActionLoader$.next(false);
  }
}
