import { CdkTextareaAutosize } from '@angular/cdk/text-field';
import {
  Component,
  ChangeDetectionStrategy,
  Input,
  Output,
  EventEmitter,
  ChangeDetectorRef,
  OnDestroy,
  OnInit,
  ViewChild,
} from '@angular/core';
import { UntypedFormControl } from '@angular/forms';
import { AuthService } from '@core/auth';
import { DomainOmsService, DomainReceiptService } from '@domain/oms';
import { HistoryModalComponent, HistoryData } from '@modal/history';
import { ComponentStore } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';

import { OrderDTO, OrderItemListItemDTO, ReceiptDTO, ReceiptType } from '@generated/swagger/oms-api';
import { UiErrorModalComponent, UiMessageModalComponent, UiModalService } from '@ui/modal';
import { isEqual } from 'lodash';
import { BehaviorSubject, combineLatest, NEVER, Subject, Observable } from 'rxjs';
import { catchError, filter, first, map, switchMap, withLatestFrom, takeUntil, take } from 'rxjs/operators';
import { SharedGoodsInOutOrderDetailsStore } from '../goods-in-out-order-details.store';

export interface SharedGoodsInOutOrderDetailsItemComponentState {
  orderItem?: OrderItemListItemDTO;
  order?: OrderDTO;
  quantity?: number;
  receipts?: ReceiptDTO[];
  selected?: boolean;
  more: boolean;
}

@Component({
  selector: 'shared-goods-in-out-order-details-item',
  templateUrl: 'goods-in-out-order-details-item.component.html',
  styleUrls: ['goods-in-out-order-details-item.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  standalone: false,
})
export class SharedGoodsInOutOrderDetailsItemComponent
  extends ComponentStore<SharedGoodsInOutOrderDetailsItemComponentState>
  implements OnInit, OnDestroy
{
  @ViewChild('autosize') autosize: CdkTextareaAutosize;

  @Input()
  get orderItem() {
    return this.get((s) => s.orderItem);
  }
  set orderItem(orderItem: OrderItemListItemDTO) {
    if (!isEqual(this.orderItem, orderItem)) {
      // Remove Prev OrderItem from selected list
      this._host.selectOrderItem(this.orderItem, false);

      this.patchState({ orderItem, quantity: orderItem?.quantity, receipts: [], more: false });
      this.specialCommentControl.reset(orderItem?.specialComment);

      // Add New OrderItem to selected list if selected was set to true by its input
      if (this.get((s) => s.selected)) {
        this._host.selectOrderItem(this.orderItem, true);
      }
    }
  }

  @Input()
  get order() {
    return this.get((s) => s.order);
  }
  set order(order: OrderDTO) {
    this.patchState({ order });
  }

  @Output()
  orderItemChange = new EventEmitter<OrderItemListItemDTO>();

  readonly orderItem$ = this.select((s) => s.orderItem);

  notificationsSent$ = this.orderItem$.pipe(
    filter((oi) => !!oi),
    switchMap((oi) =>
      this._omsService
        .getCompletedTasks({
          orderId: oi.orderId,
          orderItemId: oi.orderItemId,
          orderItemSubsetId: oi.orderItemSubsetId,
          take: 4,
          skip: 0,
        })
        .pipe(catchError(() => NEVER)),
    ),
  );

  canChangeQuantity$ = combineLatest([this.orderItem$, this._host.fetchPartial$]).pipe(
    map(([item, partialPickup]) => ([16, 8192].includes(item?.processingStatus) || partialPickup) && item.quantity > 1),
  );

  get quantity() {
    return this.get((s) => s.quantity);
  }
  set quantity(quantity: number) {
    if (this.quantity !== quantity) {
      this.patchState({ quantity });
    }
  }

  readonly quantity$ = this.select((s) => s.quantity);

  @Input()
  get selected() {
    return this._host.selectedeOrderItemSubsetIds.includes(this.orderItem?.orderItemSubsetId);
  }
  set selected(selected: boolean) {
    if (this.selected !== selected) {
      this.patchState({ selected });
      this._host.selectOrderItem(this.orderItem, selected);
    }
  }

  readonly selected$ = combineLatest([this.orderItem$, this._host.selectedeOrderItemSubsetIds$]).pipe(
    map(([orderItem, selectedItems]) => selectedItems.includes(orderItem?.orderItemSubsetId)),
  );

  @Output()
  selectedChange = new EventEmitter<boolean>();

  get selectable() {
    return this._host.itemsSelectable && this._host.orderItems.length > 1 && this._host.fetchPartial;
  }

  readonly selectable$ = combineLatest([
    this._host.orderItems$,
    this._host.itemsSelectable$,
    this._host.fetchPartial$,
  ]).pipe(map(([orderItems, selectable, fetchPartial]) => orderItems.length > 1 && selectable && fetchPartial));

  get receipts() {
    return this.get((s) => s.receipts);
  }
  set receipts(receipts: ReceiptDTO[]) {
    if (!isEqual(this.receipts, receipts)) {
      this.patchState({ receipts });
      this._host.updateReceipts(receipts);
    }
  }

  readonly receipts$ = this.select((s) => s.receipts);

  readonly receiptCount$ = this.select((s) => s.receipts?.length);

  specialCommentControl = new UntypedFormControl();

  more$ = this.select((s) => s.more);

  showMore$ = this.more$.pipe(map((more) => more || this._auth.hasRole('CallCenter')));

  fetchHistory$ = new BehaviorSubject<boolean>(false);
  private _onDestroy$ = new Subject<void>();

  constructor(
    private _host: SharedGoodsInOutOrderDetailsStore,
    private _domainReceiptService: DomainReceiptService,
    private _omsService: DomainOmsService,
    private _modal: UiModalService,
    private _cdr: ChangeDetectorRef,
    private _auth: AuthService,
  ) {
    super({
      more: false,
    });
  }

  ngOnInit() {}

  ngOnDestroy() {
    // Remove Prev OrderItem from selected list
    this._host.selectOrderItem(this.orderItem, false);
    this._onDestroy$.next();
    this._onDestroy$.complete();
  }

  loadReceipts = this.effect((done$: Observable<(receipts: ReceiptDTO[]) => void | undefined>) =>
    done$.pipe(
      withLatestFrom(this.orderItem$),
      filter(([_, orderItem]) => !!orderItem),
      switchMap(([done, orderItem]) =>
        this._domainReceiptService
          .getReceipts({
            receiptType: (1 + 64 + 128) as ReceiptType,
            ids: [orderItem.orderItemSubsetId],
            eagerLoading: 1,
          })
          .pipe(
            tapResponse(
              (res) => {
                const receipts = res.result.map((r) => r.item3?.data).filter((f) => !!f);
                this.receipts = receipts;
                if (typeof done === 'function') {
                  done?.(receipts);
                }
              },
              (err) => {
                if (typeof done === 'function') {
                  done?.([]);
                }
              },
              () => {},
            ),
          ),
      ),
    ),
  );

  async saveSpecialComment() {
    const { orderId, orderItemId, orderItemSubsetId } = this.orderItem;

    try {
      this.specialCommentControl.reset(this.specialCommentControl.value);
      const res = await this._omsService
        .patchComment({
          orderId,
          orderItemId,
          orderItemSubsetId,
          specialComment: this.specialCommentControl.value ?? '',
        })
        .pipe(first())
        .toPromise();

      this.orderItem = { ...this.orderItem, specialComment: this.specialCommentControl.value ?? '' };
      this.orderItemChange.next(this.orderItem);
      this._host.updateOrderItems([this.orderItem]);
    } catch (error) {
      console.error(error);
    }
  }

  setMore(more: boolean) {
    this.patchState({ more });
    if (more && this.receipts.length === 0) {
      this.loadReceipts(undefined);
    }
  }

  async openHistory() {
    this.fetchHistory$.next(true);
    try {
      const customerName = [
        this.orderItem?.organisation ?? this.orderItem.organisation,
        this.orderItem.lastName,
        this.orderItem.firstName,
      ]
        .filter((i) => !!i)
        .join(', ');
      const customerNumber = this.orderItem.buyerNumber;
      const history = await this._omsService
        .getHistory(this.orderItem.orderItemSubsetId)
        .pipe(takeUntil(this._onDestroy$), take(1))
        .toPromise();

      if (history) {
        if (history.length > 0) {
          const data: HistoryData = {
            customerName,
            customerNumber,
            history: history,
          };
          this._modal.open({
            content: HistoryModalComponent,
            title: 'Historie',
            config: { showScrollbarY: false, padding: false },
            data,
          });
        } else if (history.length === 0) {
          this._modal.open({
            content: UiMessageModalComponent,
            title: 'Keine Einträge in der Historie gefunden',
            data: {
              message: `Es konnten keine Einträge für ${customerName} mit der Kundennummer ${customerNumber} gefunden werden.`,
            },
          });
        }
      }
    } catch (error) {
      const errorModalRef = this._modal.open({
        content: UiErrorModalComponent,
        title: 'Abruf der Historie fehlgeschlagen',
        data: error,
      });
      await errorModalRef.afterClosed$.toPromise();
    } finally {
      this.fetchHistory$.next(false);
    }
  }

  setSelected(selected: boolean) {
    this.selected = selected;
    this.selectedChange.emit(selected);
    this._cdr.markForCheck();
  }

  orderItemFeature(orderItemListItem: OrderItemListItemDTO) {
    const orderItems = this.order?.items;
    return orderItems?.find((orderItem) => orderItem.data.id === orderItemListItem.orderItemId)?.data?.features
      ?.orderType;
  }

  triggerResize() {
    this.autosize.reset();
  }
}
