import {
  AfterViewInit,
  ChangeDetectionStrategy,
  Component,
  ContentChild,
  ContentChildren,
  OnDestroy,
  OnInit,
  QueryList,
} from '@angular/core';
import { ActivatedRoute, NavigationError, Router } from '@angular/router';
import { BreadcrumbService } from '@core/breadcrumb';
import { EnvironmentService } from '@core/environment';
import { OrderItemsContext } from '@domain/oms';
import { provideComponentStore } from '@ngrx/component-store';
import { CustomerOrdersNavigationService } from '@shared/services/navigation';
import {
  KeyValueDTOOfStringAndString,
  OrderItemListItemDTO,
  OrderItemProcessingStatusValue,
  ReceiptDTO,
} from '@generated/swagger/oms-api';
import { UiErrorModalComponent, UiMessageModalComponent, UiModalService } from '@ui/modal';
import { BehaviorSubject, Subscription, combineLatest } from 'rxjs';
import { first, map } from 'rxjs/operators';
import { CustomerOrderDetailsStore } from './customer-order-details.store';
import { CustomerOrderDetailsItemComponent } from './customer-order-details-item';
import { unionBy } from 'lodash';
import { CommandService } from '@core/command';
import { CustomerOrderDetailsTagsComponent } from './customer-order-details-tags';

@Component({
  selector: 'page-customer-order-details',
  templateUrl: 'customer-order-details.component.html',
  styleUrls: ['customer-order-details.component.scss'],
  providers: [provideComponentStore(CustomerOrderDetailsStore)],
  changeDetection: ChangeDetectionStrategy.OnPush,
  standalone: false,
})
export class CustomerOrderDetailsComponent implements OnInit, AfterViewInit, OnDestroy {
  @ContentChildren(CustomerOrderDetailsItemComponent)
  customerOrderDetailsItemComponents: QueryList<CustomerOrderDetailsItemComponent>;

  @ContentChild(CustomerOrderDetailsTagsComponent, { static: false })
  customerOrderDetailsTags: CustomerOrderDetailsTagsComponent;

  buyerNumber$ = this._store.buyerNumber$;

  compartmentCode$ = this._store.compartmentCode$;

  latestCompartmentCode$ = this._store.latestCompartmentCode$;

  latestCompartmentCode = this._store.latestCompartmentCode;

  latestCompartmentInfo = this._store.latestCompartmentInfo;

  compartmentInfo$ = this._store.compartmentInfo$;

  compartmentInfo = this._store.compartmentInfo;

  addToPreviousCompartmentAction$ = this._store.addToPreviousCompartmentAction$;

  mainActions$ = this._store.mainActions$;

  processingStatus$ = this._store.processingStatus$;

  items$ = this._store.items$;

  order$ = this._store.order$;

  fetching$ = this._store.fetching$;

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

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

  private subscriptions = new Subscription();

  get isDesktop() {
    return this._environment.matchDesktop();
  }

  changeActionLoader$ = new BehaviorSubject<string>(undefined);
  changeActionDisabled$ = this._store.changeActionDisabled$;

  showTagsComponent$ = combineLatest([this.items$, this.fetching$]).pipe(
    map(([items, fetching]) => {
      const first = items?.find((_) => true);
      const hasArrivedAction = first?.actions?.some((a) => a.command?.includes('ARRIVED'));
      return hasArrivedAction && !fetching;
    }),
  );

  actionsDisabled$ = combineLatest([
    this.changeActionDisabled$,
    this._store.select((s) => (s.fetchPartial ? s.selectedeOrderItemSubsetIds.length === 0 : false)),
    this.items$,
  ]).pipe(map(([disabled, partial, orderItems]) => disabled || (partial && orderItems.length > 1)));

  addToPreviousCompartmentActionDisabled$ = combineLatest([
    this.compartmentInfo$,
    this.changeActionDisabled$,
    this.fetching$,
  ]).pipe(
    map(([compartmentInfo, changeActionDisabled, fetching]) => (!!compartmentInfo || changeActionDisabled) && fetching),
  );

  constructor(
    private _activatedRoute: ActivatedRoute,
    private _store: CustomerOrderDetailsStore,
    private _navigationService: CustomerOrdersNavigationService,
    private _breadcrumb: BreadcrumbService,
    private _router: Router,
    private _uiModal: UiModalService,
    private _environment: EnvironmentService,
    private _commandService: CommandService,
  ) {}

  ngOnInit() {
    this.subscriptions.add(
      this._activatedRoute.queryParams.subscribe((params) => {
        const buyerNumber: string = decodeURIComponent(params.buyerNumber ?? '');
        this._store.patchState({ buyerNumber });
      }),
    );

    this.subscriptions.add(
      this._activatedRoute.params.subscribe(async (params) => {
        const orderId = params?.orderId;
        const compartmentCode = params?.compartmentCode;
        const processingStatus = +params?.processingStatus as OrderItemProcessingStatusValue;

        if (
          this._store.orderId !== orderId ||
          this._store.compartmentCode !== compartmentCode ||
          this._store.processingStatus !== processingStatus
        ) {
          this._store.patchState({
            orderId: orderId ? +orderId : undefined,
            compartmentCode: compartmentCode ? decodeURIComponent(compartmentCode) : undefined,
            processingStatus,
          });
          this._store.loadItems();
        }

        await this.removeDetailsCrumbs();
      }),
    );

    this.subscriptions.add(
      this._store.searchCompleted.subscribe((state) => {
        const items = state?.items;
        this.openModalIfItemsHaveDifferentCustomers(items);
        this.itemsSelectable(items);

        const firstItem = items?.find((_) => true);
        this.updateBreadcrumb(firstItem);

        const orderId = firstItem?.orderId;
        if (!!orderId && this._store?.order?.id !== orderId) {
          this._store.patchState({ orderId });
          this._store.loadOrder();
        }
      }),
    );

    this.removeBreadcrumbs();
  }

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

  ngOnDestroy() {
    this.subscriptions.unsubscribe();
  }

  private _registerCustomerOrderDetailsTagsComponentChanges() {
    this.subscriptions.add(
      this.items$.subscribe((_) => {
        this.customerOrderDetailsTags?.writeValue(this.compartmentInfo);
        this.customerOrderDetailsTags?.registerOnChange(
          (compartmentInfo) => (this._store.compartmentInfo = compartmentInfo),
        );
      }),
    );
  }

  itemsSelectable(items: OrderItemListItemDTO[]) {
    this._store.patchState({
      itemsSelectable: items?.some((item) => !!item?.actions && item?.actions?.length > 0),
    });
  }

  async updateBreadcrumb(item: OrderItemListItemDTO) {
    if (item) {
      await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
        key: this.processId,
        name: item?.compartmentCode || item?.orderNumber,
        path: this.getDetailsPath(item),
        params: {
          ...this._activatedRoute.snapshot.queryParams,
          buyerNumber: item.buyerNumber,
        },
        section: 'customer',
        tags: ['customer-order', 'details', item?.compartmentCode || item?.orderNumber, String(item?.orderId)],
      });
    }
  }

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

    const historyCrumbs = await this._breadcrumb
      .getBreadcrumbsByKeyAndTags$(this.processId, ['customer-order', 'history'])
      .pipe(first())
      .toPromise();

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

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

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

  openModalIfItemsHaveDifferentCustomers(items: OrderItemListItemDTO[]) {
    const buyerNumbers = new Set(items?.map((item) => item?.buyerNumber));
    if (buyerNumbers.size > 1) {
      this._uiModal.open({
        content: UiMessageModalComponent,
        title: 'Achtung',
        data: {
          message:
            'Anzeige ist fehlerhaft.\nEs wurden mehrere Positionen gefunden, die mehreren Kunden unterschiedlichen sind.',
        },
      });
    }
  }

  navigateToEditPage(orderItem: OrderItemListItemDTO) {
    this._router.navigate(this.getEditPath(orderItem), {
      queryParams: { ...this._activatedRoute.snapshot.queryParams, buyerNumber: orderItem?.buyerNumber },
    });
  }

  navigateToHistoryPage(orderItem: OrderItemListItemDTO) {
    this._router.navigate(this.getHistoryPath(orderItem), {
      queryParams: {
        ...this._activatedRoute.snapshot.queryParams,
        buyerNumber: orderItem?.buyerNumber,
        orderItemSubsetId: orderItem?.orderItemSubsetId,
      },
    });
  }

  getDetailsPath(item: OrderItemListItemDTO) {
    return this._navigationService.getCustomerOrdersDetailsPath({
      processId: this.processId,
      processingStatus: item?.processingStatus,
      compartmentCode: item?.compartmentCode ? encodeURIComponent(item.compartmentCode) : undefined,
      orderId: item?.orderId,
    }).path;
  }

  getEditPath(item: OrderItemListItemDTO) {
    return this._navigationService.getCustomerOrdersEditPath({
      processId: this.processId,
      processingStatus: item?.processingStatus,
      compartmentCode: item?.compartmentCode ? encodeURIComponent(item.compartmentCode) : undefined,
      orderId: item?.orderId,
    }).path;
  }

  getHistoryPath(item: OrderItemListItemDTO) {
    return this._navigationService.getCustomerOrdersHistoryPath({
      processId: this.processId,
      processingStatus: item?.processingStatus,
      compartmentCode: item?.compartmentCode ? encodeURIComponent(item.compartmentCode) : undefined,
      orderId: item?.orderId,
    }).path;
  }

  getItemQuantityMap(): Map<number, number> {
    return new Map(
      this.customerOrderDetailsItemComponents
        .toArray()
        .map((component) => [component.orderItem.orderItemSubsetId, component.quantity]),
    );
  }

  getitemsToUpdate(): OrderItemListItemDTO[] {
    if (this._store.fetchPartial && this._store.itemsSelectable && this._store.items.length > 1) {
      return this._store.items.filter((orderItem) =>
        this._store.selectedeOrderItemSubsetIds.includes(orderItem.orderItemSubsetId),
      );
    }

    return this._store.items;
  }

  async handleAction(
    action: KeyValueDTOOfStringAndString,
    { compartmentCode, compartmentInfo }: { compartmentCode?: string; compartmentInfo?: string } = {},
  ) {
    if (action.command.includes('FETCHED_PARTIAL')) {
      this._store.patchState({ fetchPartial: true });
      return;
    }
    let receipts: ReceiptDTO[] = [];
    if (action.command.includes('PRINT_SHIPPINGNOTE')) {
      const receiptsPromise = this.customerOrderDetailsItemComponents.toArray().map(
        (itemComponent) =>
          new Promise<ReceiptDTO[]>((resolve) => {
            itemComponent.loadReceipts((r) => resolve(r));
          }),
      );

      receipts = await Promise.all(receiptsPromise).then((r) => r.reduce((acc, val) => acc.concat(val), []));

      receipts = unionBy(receipts, 'id');
    }

    // #2737 Bei Zubuchen kein Abholfachzettel ausdrucken
    let command = action.command;
    if (compartmentCode) {
      command = action.command.replace('|PRINT_COMPARTMENTLABEL', '');
    }

    this.changeActionLoader$.next(action.command);
    this.changeActionDisabled$.next(true);

    let commandData: OrderItemsContext = {
      items: this.getitemsToUpdate(),
      compartmentInfo: compartmentInfo || this.compartmentInfo,
      compartmentCode:
        action.command.includes('PRINT_PRICEDIFFQRCODELABEL') && !compartmentCode
          ? this._store.items?.find((_) => true)?.compartmentCode
          : compartmentCode,
      itemQuantity: this.getItemQuantityMap(),
      receipts,
    };
    try {
      commandData = await this._commandService.handleCommand(command, commandData);
      let navigateTo: 'details' | 'main' | 'reservation' = 'details';
      if (action.command.includes('ARRIVED')) {
        navigateTo = await this.arrivedActionNavigation();
      }
      if (action.command.includes('PRINT_PRICEDIFFQRCODELABEL') || action.command.includes('BACKTOSTOCK')) {
        navigateTo = 'main';
      }

      await this.actionHandled({ orderItemsContext: commandData, command: action.command, navigation: navigateTo });
      this._store.updateOrderItems(commandData.items);
    } catch (error) {
      if (error instanceof NavigationError) {
        await this.handleRouterMatchNoRoutesError(); // Refresh Result list but stay on same page
      } else {
        this._uiModal.open({
          content: UiErrorModalComponent,
          data: error,
        });
      }
      console.error(error);
    }

    if (action.command.includes('FETCHED_PARTIAL')) {
      this._store.patchState({ fetchPartial: false });
    }
    this._store.selectedeOrderItemSubsetIds = [];

    setTimeout(() => {
      this.changeActionDisabled$.next(false);
      this.changeActionLoader$.next(undefined);
    }, 1000);
  }

  async actionHandled(handler: {
    orderItemsContext: OrderItemsContext;
    command: string;
    navigation: 'details' | 'main' | 'reservation';
  }) {
    if (handler.navigation === 'main') {
      await this._navigationService.getCustomerOrdersBasePath(this.processId).navigate();
    } else {
      const item: OrderItemListItemDTO = handler?.orderItemsContext?.items?.find((_) => true);
      if (item) {
        await this._router.navigate(this.getDetailsPath(item), {
          queryParams: { ...this._activatedRoute.snapshot.queryParams, buyerNumber: item.buyerNumber },
        });
      }
      if (this.isDesktop) {
        await this.refreshResults();
      }
      await this.removeDetailsCrumbs();
      this._store.loadItems();
    }
  }

  async handleRouterMatchNoRoutesError() {
    await this.refreshResults();
  }

  async onSpecialCommentChange() {
    if (this.isDesktop) {
      await this.refreshResults();
    }
  }

  async refreshResults() {
    await this._router.navigate([], {
      queryParams: { ...this._activatedRoute.snapshot.queryParams, updateResults: true },
    });
  }

  async arrivedActionNavigation(): Promise<'main'> {
    const detailsCrumbs = await this._breadcrumb
      .getBreadcrumbsByKeyAndTags$('customer-order', ['customer-order', 'details'])
      .pipe(first())
      .toPromise();
    detailsCrumbs.forEach((crumb) => {
      this._breadcrumb.removeBreadcrumb(crumb.id, true);
    });

    return 'main';
  }
}
