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 { FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { DomainOmsService } from '@domain/oms';
import { emailNotificationValidator, mobileNotificationValidator } from '@shared/notification-channel-control';
import { NotificationChannel, OrderItemListItemDTO, StockStatusCodeDTO, VATDTO } from '@swagger/oms';
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 { EnvironmentChannelPipe } from '../pipes/environment-channel.pipe';
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: [EnvironmentChannelPipe, ProcessingStatusPipe, DatePipe],
})
export class SharedGoodsInOutOrderEditComponent implements OnChanges, OnDestroy {
  @ViewChildren('autosize') autosize: QueryList<CdkTextareaAutosize>;

  @Output()
  navigation = new EventEmitter<{ options?: { processingStatus?: number } }>();

  @Input()
  items: OrderItemListItemDTO[];

  expanded: boolean[];

  showTagsComponent: boolean[];

  control: FormGroup;

  notificationsGroup = new FormGroup({
    selected: new FormControl(1),
    email: new FormControl('', emailNotificationValidator),
    mobile: new FormControl('', 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(): FormArray {
    return this.control.get('items') as FormArray;
  }

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

  constructor(
    private fb: FormBuilder,
    private environmentChannelPipe: EnvironmentChannelPipe,
    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();
    }
  }

  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: this.environmentChannelPipe.transform(items[0].clientChannel), disabled: true }),
      buyerNumber: fb.control({ value: items[0].buyerNumber, disabled: true }),
      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(item.price ? String(item.price).replace('.', ',') : '', [
          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() {
    const control = this.control?.getRawValue();
    const orderId = control?.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();
  }

  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;
  }

  navigateBack({ processingStatus }: { processingStatus?: number }) {
    processingStatus ? this.navigation.emit({ options: { processingStatus } }) : this.navigation.emit({});
  }

  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;

      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: number;
      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.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)) {
            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;
        }
      }
      this.navigateBack({ processingStatus: newProcessingStatus });
    } catch (error) {
      console.error(error);
      this.enableControlFields();
    }
  }

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

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

  isOrderItemSubsetDirty(group: FormGroup) {
    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);
  }
}
