import { CdkTextareaAutosize } from '@angular/cdk/text-field';
import { DatePipe } from '@angular/common';
import {
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  EventEmitter,
  Input,
  OnChanges,
  OnDestroy,
  Output,
  QueryList,
  SimpleChanges,
  ViewChildren,
} from '@angular/core';
import { UntypedFormArray, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms';
import { DomainOmsService } from '@domain/oms';
import {
  emailNotificationValidator,
  mobileNotificationValidator,
} from '@shared/components/notification-channel-control';
import {
  DBHOrderItemListItemDTO,
  NotificationChannel,
  OrderItemListItemDTO,
  OrderItemProcessingStatusValue,
  OrderItemSubsetDTO,
  StockStatusCodeDTO,
  VATDTO,
} from '@generated/swagger/oms-api';
import { DateAdapter } from '@ui/common';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
import { UiSelectOptionComponent } from '@ui/select';
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { first, shareReplay } from 'rxjs/operators';
import { ProcessingStatusNameMap } from '../constants/processing-status-name.map';
import { ProcessingStatusPipe } from '../pipes/processing-status.pipe';
import { validateSsc } from '../validators/ssc.validator';

@Component({
  selector: 'shared-goods-in-out-order-edit',
  templateUrl: 'goods-in-out-order-edit.component.html',
  styleUrls: ['goods-in-out-order-edit.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [ProcessingStatusPipe, DatePipe],
  standalone: false,
})
export class SharedGoodsInOutOrderEditComponent implements OnChanges, OnDestroy {
  @ViewChildren('autosize') autosize: QueryList<CdkTextareaAutosize>;

  @Output()
  navigation = new EventEmitter<Partial<DBHOrderItemListItemDTO>>();

  @Input()
  items: OrderItemListItemDTO[];

  expanded: boolean[];

  showTagsComponent: boolean[];

  control: UntypedFormGroup;

  notificationsGroup = new UntypedFormGroup({
    selected: new UntypedFormControl(1),
    email: new UntypedFormControl('', emailNotificationValidator),
    mobile: new UntypedFormControl('', mobileNotificationValidator),
  });

  minDate = this.dateAdapter.addCalendarDays(new Date(), -1);

  vats$: Observable<VATDTO[]> = this.omsService.getVATs().pipe(shareReplay());

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

  private _subscriptions = new Subscription();

  displayDate = (option: UiSelectOptionComponent) => {
    return this.datePipe.transform(option.value, 'dd.MM.yy');
  };

  public get itemsControl(): UntypedFormArray {
    return this.control.get('items') as UntypedFormArray;
  }

  public get processingStatus(): number[] {
    return Array.from(ProcessingStatusNameMap.keys());
  }

  get showNameFields(): boolean {
    // orderType 1 === Abholung / Rücklage
    return this.items[0]?.orderType === 1; // Felder nur bei "Rücklage" oder "Abholung" anzeigen #4687
  }

  get canEditNameFields(): boolean {
    return this.items[0]?.processingStatus === 16; // Felder nur im Status bestellt bearbeitbar #4687
  }

  // #4687 Ungenauer B2B-Check, da Customer Features nicht am OrderItemListItemDTO hängen
  get isB2B(): boolean {
    return !!this.items[0]?.organisation;
  }

  constructor(
    private fb: UntypedFormBuilder,
    private processingStatusPipe: ProcessingStatusPipe,
    private datePipe: DatePipe,
    private omsService: DomainOmsService,
    private dateAdapter: DateAdapter,
    private cdr: ChangeDetectorRef,
    private _modal: UiModalService,
  ) {}

  ngOnDestroy(): void {
    this._subscriptions?.unsubscribe();
  }

  ngOnChanges({ items }: SimpleChanges) {
    if (items?.currentValue) {
      this.expanded = new Array(items.currentValue.length);
      this.expanded[0] = true;

      this.showTagsComponent = items.currentValue?.map((item) => !!item.compartmentCode);
      this.initForm(items.currentValue);
      this.updateNotificationsGroup();
    }
  }

  isKulturpass(item: OrderItemListItemDTO): boolean {
    return item?.['externalRepositories']?.includes('kulturpass');
  }

  async initForm(items: OrderItemListItemDTO[]) {
    if (items.length === 0) {
      return;
    }

    const fb = this.fb;
    this.control = fb.group({
      orderId: fb.control({ value: items[0].orderId, disabled: true }),
      orderNumber: fb.control({ value: items[0].orderNumber, disabled: true }),
      orderDate: fb.control({ value: this.datePipe.transform(items[0].orderDate), disabled: true }),
      clientChannel: fb.control({
        value: (await this.getOrderSource()) ?? items[0].features?.orderSource,
        disabled: true,
      }),
      buyerNumber: fb.control({ value: items[0].buyerNumber, disabled: true }),
      firstName: fb.control({ value: items[0].firstName, disabled: !this.canEditNameFields }),
      lastName: fb.control({ value: items[0].lastName, disabled: !this.canEditNameFields }),
      organisation: fb.control({ value: items[0].organisation, disabled: !this.canEditNameFields }),
      items: fb.array([]),
      notificationChannel: this.notificationsGroup,
    });

    items.forEach(async (item, index) => {
      let statusCodes: StockStatusCodeDTO[];
      if (item.supplierId) {
        try {
          statusCodes = await this.omsService
            .getStockStatusCodes({ supplierId: item.supplierId })
            .pipe(first())
            .toPromise();
        } catch (error) {}
      }
      const fbItem = fb.group({
        compartmentCode: fb.control(item.compartmentCode),
        compartmentInfo: fb.control(item.compartmentInfo),
        orderItemId: fb.control(item.orderItemId),
        orderItemSubsetId: fb.control(item.orderItemSubsetId),
        processingStatus: fb.control({
          value: this.processingStatusPipe.transform(item.processingStatus),
          disabled: true,
        }),
        estimatedShippingDate: fb.control(item.estimatedShippingDate),
        pickUpDeadline: fb.control({
          value: item.pickUpDeadline,
          disabled: !!item?.features?.paid && item?.processingStatus === 128,
        }),
        processingStatusDate: fb.control({
          value: this.datePipe.transform(item.processingStatusDate, 'dd.MM.yy | HH:mm') + ' Uhr',
          disabled: true,
        }),
        name: fb.control(item.product?.name),
        ean: fb.control(item.product?.ean, [Validators.required]),
        quantity: fb.control({ value: item.quantity + ' x', disabled: true }),
        price: fb.control(this.formatPrice(item?.price), [
          Validators.required,
          Validators.pattern(/^\d+([\,]\d{1,2})?$/),
        ]),
        currency: fb.control(item.currency),
        targetBranch: fb.control({ value: item.targetBranch, disabled: true }),
        supplier: fb.control({ value: item.supplier, disabled: true }),
        ssc: fb.control(item.ssc, [Validators.required, validateSsc(statusCodes)]),
        sscText: fb.control({ value: '', disabled: true }),
        isPrebooked: fb.control(!!item.isPrebooked),
        vat: fb.control(item.vatType),
        specialComment: fb.control(item.specialComment),
      });
      this._subscriptions.add(
        fbItem.get('compartmentCode').valueChanges.subscribe((value) => {
          this.showTagsComponent[index] = !!value;

          if (!value) {
            fbItem.get('compartmentInfo').reset('');
          }
        }),
      );

      this.itemsControl.push(fbItem);
      this.cdr.markForCheck();
    });
  }

  async updateNotificationsGroup() {
    if (this.items.length === 0) {
      return;
    }
    const orderId = this.items[0].orderId;

    try {
      if (orderId) {
        const notifications = await this.omsService.getNotifications(+orderId).toPromise();

        this.notificationsGroup.reset(notifications);
      }
    } catch (error) {
      this._modal.open({
        content: UiErrorModalComponent,
        data: error,
        title: 'Fehler beim abrufen der Benachrichtigung',
      });
    }
  }

  async patchNotifications() {
    const control = this.control.getRawValue();
    const orderId = control.orderId;
    await this.omsService.updateNotifications(orderId, this.notificationsGroup.getRawValue()).toPromise();
  }

  async getOrderSource() {
    const orderId = this.items[0].orderId;
    return this.omsService.getOrderSource(+orderId).toPromise();
  }

  formatPrice(price: number) {
    if (!price) {
      return '';
    }

    const priceWithTwoDecimalPlaces = price.toFixed(2);
    return String(priceWithTwoDecimalPlaces).replace('.', ',');
  }

  changeEstimatedDeliveryDate(date: Date, item: OrderItemListItemDTO) {
    if (!date) {
      return;
    }
    const estimatedShippingDate = date.toISOString();
    item.estimatedShippingDate = estimatedShippingDate;
  }

  changePickupDeadline(date: Date, item: OrderItemListItemDTO) {
    if (!date) {
      return;
    }
    const deadline = date.toISOString();
    item.pickUpDeadline = deadline;
  }

  async generateNotification(notificationChannels: NotificationChannel[]) {
    if (!notificationChannels || notificationChannels.length === 0) {
      return;
    }

    this.notificationChannelLoading$.next(true);

    try {
      await this.patchNotifications();
      await this.omsService
        .generateNotifications({
          orderId: this.items[0].orderId,
          taskTypes:
            notificationChannels.length === 2
              ? ['email', 'sms']
              : notificationChannels[0] === 1
                ? ['email']
                : notificationChannels[0] === 2
                  ? ['sms']
                  : [],
        })
        .pipe(first())
        .toPromise();
    } catch (error) {
      this._modal.open({ content: UiErrorModalComponent, data: error, title: 'Fehler beim erneut Benachrichtigen' });
      throw error;
    }
    this.notificationChannelLoading$.next(false);
  }

  async submit() {
    if (this.control.invalid || this.control.disabled) {
      return;
    }

    this.control.disable();

    try {
      const control = this.control.getRawValue();
      const orderId = control.orderId;
      const firstName = control.firstName;
      const lastName = control.lastName;
      const organisation = control.organisation;

      if (this.notificationsGroup.dirty) {
        try {
          await this.patchNotifications();
        } catch (error) {
          this._modal.open({
            content: UiErrorModalComponent,
            data: error,
            title: 'Fehler beim Aktualisieren der Benachrichtigung',
          });
          throw error;
        }
      }

      let newProcessingStatus: OrderItemProcessingStatusValue;
      let changedOrderItemSubsets: OrderItemSubsetDTO[] = [];
      for (let itemCtrl of control.items) {
        const orderItemId = itemCtrl.orderItemId;
        const orderItemSubsetId = itemCtrl.orderItemSubsetId;
        const formGroup = this.getFormGroupByOrderItemSubsetId(orderItemSubsetId);

        try {
          const currentItem = this.items?.find((i) => i.orderItemSubsetId === orderItemSubsetId);
          if (itemCtrl.ssc !== currentItem?.ssc) {
            const response = await this.omsService
              .changeStockStatusCode([{ id: orderItemSubsetId, ssc: itemCtrl.ssc, quantity: currentItem.quantity }])
              .toPromise();
            newProcessingStatus = response[0].item1.processingStatus;
            itemCtrl = {
              ...itemCtrl,
              estimatedShippingDate: response[0].item1.estimatedShippingDate,
              isPrebooked: response[0].item1.isPrebooked,
            };
          }
        } catch (error) {
          this._modal.open({
            content: UiErrorModalComponent,
            data: error,
            title: 'Fehler beim Aktualisieren der Meldenummer',
          });
          throw error;
        }

        try {
          if (this.firstOrLastNameOrOrganisationChanged()) {
            await this.omsService.updateOrder({ orderId, firstName, lastName, organisation }).pipe(first()).toPromise();
          }
        } catch (error) {
          this._modal.open({
            content: UiErrorModalComponent,
            data: error,
            title: 'Fehler beim Aktualisieren der Bestellung - Vorname und Name konnten nicht übernommen werden',
          });
          throw error;
        }

        try {
          if (this.isOrderItemDirty(formGroup)) {
            await this.omsService
              .patchOrderItem({
                orderId,
                orderItemId,
                orderItem: {
                  product: { ean: itemCtrl.ean },
                  grossPrice: {
                    vat: { vatType: itemCtrl.vat },
                    value: { value: Number(String(itemCtrl.price).replace(',', '.')) },
                  },
                },
              })
              .pipe(first())
              .toPromise();
          }
        } catch (error) {
          this._modal.open({
            content: UiErrorModalComponent,
            data: error,
            title: 'Fehler beim Aktualisieren des Bestellpostens',
          });
          throw error;
        }

        try {
          if (this.isOrderItemSubsetDirty(formGroup)) {
            changedOrderItemSubsets.push(
              await this.omsService
                .patchOrderItemSubset({
                  orderId,
                  orderItemId,
                  orderItemSubsetId,
                  orderItemSubset: {
                    compartmentCode:
                      itemCtrl.compartmentInfo && itemCtrl.compartmentCode
                        ? itemCtrl.compartmentCode.replace('_' + itemCtrl.compartmentInfo, '')
                        : itemCtrl.compartmentCode,
                    compartmentInfo: itemCtrl.compartmentInfo || '',
                    estimatedShippingDate: itemCtrl.estimatedShippingDate || null,
                    compartmentStop: itemCtrl.pickUpDeadline || null,
                    specialComment: itemCtrl.specialComment || '',
                    ssc: itemCtrl.ssc,
                    sscText: itemCtrl.sscText !== '' ? itemCtrl.sscText.substring(3) : '',
                    isPrebooked: itemCtrl.isPrebooked,
                  },
                })
                .pipe(first())
                .toPromise(),
            );
          }
        } catch (error) {
          this._modal.open({
            content: UiErrorModalComponent,
            data: error,
            title: 'Fehler beim Aktualisieren des Bestellpostens',
          });
          throw error;
        }
      }

      if (changedOrderItemSubsets.length === 0) {
        this.navigation.emit({});
        return;
      }

      if (changedOrderItemSubsets.length === 1) {
        this.navigation.emit({
          processingStatus: newProcessingStatus,
          compartmentCode: changedOrderItemSubsets[0].compartmentCode,
          compartmentInfo: changedOrderItemSubsets[0].compartmentInfo,
        });
        return;
      }

      this.navigation.emit({
        processingStatus: newProcessingStatus,
      });
    } catch (error) {
      console.error(error);
      this.enableControlFields();
    }
  }

  firstOrLastNameOrOrganisationChanged() {
    return (
      this.control.get('firstName').dirty ||
      this.control.get('lastName').dirty ||
      this.control.get('organisation').dirty
    );
  }

  getFormGroupByOrderItemSubsetId(orderItemSubsetId: number): UntypedFormGroup {
    const arr = this.control.get('items') as UntypedFormArray;
    return arr.controls.find(
      (c) => (c as UntypedFormGroup).controls.orderItemSubsetId.value === orderItemSubsetId,
    ) as UntypedFormGroup;
  }

  isOrderItemDirty(group: UntypedFormGroup) {
    return group.controls.ean.dirty || group.controls.vat.dirty || group.controls.price.dirty;
  }

  isOrderItemSubsetDirty(group: UntypedFormGroup) {
    return (
      group.controls.compartmentCode.dirty ||
      group.controls.compartmentInfo.dirty ||
      group.controls.estimatedShippingDate.dirty ||
      group.controls.pickUpDeadline.dirty ||
      group.controls.specialComment.dirty ||
      group.controls.isPrebooked.dirty
    );
  }

  // this.control.enable() would enable initially disabled fields too
  enableControlFields() {
    this.itemsControl.controls.forEach((ctrl) => {
      ctrl.get('compartmentCode').enable();
      ctrl.get('estimatedShippingDate').enable();
      ctrl.get('pickUpDeadline').enable();
      ctrl.get('name').enable();
      ctrl.get('ean').enable();
      ctrl.get('price').enable();
      ctrl.get('ssc').enable();
      ctrl.get('vat').enable();
      ctrl.get('specialComment').enable();
    });
  }

  triggerResize(index: number) {
    this.autosize?.get(index)?.reset();
  }

  clearSpecialComment(index: number) {
    this.itemsControl.at(index).get('specialComment')?.reset('');
    this.itemsControl.at(index).get('specialComment')?.markAsDirty();
    this.triggerResize(index);
  }
}
