import {
  Component,
  ChangeDetectionStrategy,
  OnInit,
  OnDestroy,
  TrackByFunction,
  HostBinding,
} from '@angular/core';
import { UiModalRef } from '@ui/modal';
import { PurchaseOptionsModalContext } from './purchase-options-modal.data';

import { PurchaseOptionsListHeaderComponent } from './purchase-options-list-header';
import { PurchaseOptionsListItemComponent } from './purchase-options-list-item';
import { CommonModule } from '@angular/common';
import { Subject, zip } from 'rxjs';
import {
  DeliveryPurchaseOptionTileComponent,
  DownloadPurchaseOptionTileComponent,
  InStorePurchaseOptionTileComponent,
  PickupPurchaseOptionTileComponent,
} from './purchase-options-tile';
import {
  isDownload,
  isGiftCard,
  Item,
  PurchaseOption,
  PurchaseOptionsStore,
} from './store';
import {
  delay,
  map,
  shareReplay,
  skip,
  switchMap,
  takeUntil,
  tap,
} from 'rxjs/operators';
import { KeyValueDTOOfStringAndString } from '@generated/swagger/cat-search-api';
import { provideComponentStore } from '@ngrx/component-store';

@Component({
  selector: 'shared-purchase-options-modal',
  templateUrl: 'purchase-options-modal.component.html',
  styleUrls: ['purchase-options-modal.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [provideComponentStore(PurchaseOptionsStore)],
  imports: [
    CommonModule,
    PurchaseOptionsListHeaderComponent,
    PurchaseOptionsListItemComponent,
    DeliveryPurchaseOptionTileComponent,
    InStorePurchaseOptionTileComponent,
    PickupPurchaseOptionTileComponent,
    DownloadPurchaseOptionTileComponent,
  ],
})
export class PurchaseOptionsModalComponent implements OnInit, OnDestroy {
  get type() {
    return this._uiModalRef.data.type;
  }

  @HostBinding('attr.data-loading')
  get fetchingData() {
    return this.store.fetchingAvailabilities.length > 0;
  }

  items$ = this.store.items$;

  hasPrice$ = this.items$.pipe(
    switchMap((items) =>
      items.map((item) => {
        let isArchive = false;
        const features = item?.features as KeyValueDTOOfStringAndString[];
        // Ticket #4074 analog zu Ticket #2244
        // Ob Archivartikel kann nur über Kaufoptionen herausgefunden werden, nicht über Ändern im Warenkorb da am ShoppingCartItem das Archivartikel Feature fehlt
        if (!!features && Array.isArray(features)) {
          isArchive = !!features?.find(
            (feature) => feature?.enabled === true && feature?.key === 'ARC',
          );
        }
        return zip(
          this.store
            ?.getPrice$(item?.id)
            .pipe(
              map((price) =>
                isArchive
                  ? !!price?.value?.value &&
                    price?.vat !== undefined &&
                    price?.vat?.value !== undefined
                  : !!price?.value?.value,
              ),
            ),
        );
      }),
    ),
    switchMap((hasPrices) => hasPrices),
    map((hasPrices) => {
      const containsItemWithNoPrice =
        hasPrices?.filter((hasPrice) => hasPrice === false) ?? [];
      return containsItemWithNoPrice?.length === 0;
    }),
  );

  purchasingOptions$ = this.store.getPurchaseOptionsInAvailabilities$;

  isDownloadOnly$ = this.store.items$.pipe(
    map((items) => items.length > 0 && items.every((item) => isDownload(item))),
  );

  isGiftCardOnly$ = this.store.items$.pipe(
    map((items) => items.every((item) => isGiftCard(item, this.store.type))),
  );

  hasDownload$ = this.store.items$.pipe(
    map((items) => items.some((item) => isDownload(item))),
  );

  canContinue$ = this.store.canContinue$;

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

  saving = false;

  constructor(
    private _uiModalRef: UiModalRef<string, PurchaseOptionsModalContext>,
    public store: PurchaseOptionsStore,
  ) {
    this.store.initialize(this._uiModalRef.data);
  }

  ngOnInit(): void {
    this.items$
      .pipe(takeUntil(this._onDestroy$), skip(1), delay(100))
      .subscribe((items) => {
        if (items.length === 0) {
          this._uiModalRef.close();
          return;
        }

        if (this._uiModalRef.data?.preSelectOption?.option) {
          this.store.setPurchaseOption(
            this._uiModalRef.data?.preSelectOption?.option,
          );
        }
      });
  }

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

  itemTrackBy: TrackByFunction<Item> = (_, item) => item.id;

  /**
   * Determines if a purchase option should be shown in the UI.
   *
   * Evaluation order:
   * 1. If option is disabled AND hideDisabledPurchaseOptions is true -> hide (return false)
   * 2. If preSelectOption.showOptionOnly is true -> show only that option
   * 3. Otherwise -> show the option
   *
   * @param option - The purchase option to check
   * @returns true if the option should be displayed, false otherwise
   *
   * @example
   * ```typescript
   * // In template
   * @if (showOption('delivery')) {
   *   <app-delivery-purchase-options-tile></app-delivery-purchase-options-tile>
   * }
   * ```
   */
  showOption(option: PurchaseOption): boolean {
    const disabledOptions = this._uiModalRef.data?.disabledPurchaseOptions ?? [];
    const hideDisabled = this._uiModalRef.data?.hideDisabledPurchaseOptions ?? true;

    if (disabledOptions.includes(option) && hideDisabled) {
      return false;
    }
    return this._uiModalRef.data?.preSelectOption?.showOptionOnly
      ? this._uiModalRef.data?.preSelectOption?.option === option
      : true;
  }

  async save(action: string) {
    if (this.saving) {
      return;
    }
    this.saving = true;

    try {
      await this.store.save();

      if (this.store.items.length === 0) {
        this._uiModalRef.close(action);
      }
    } catch (error) {
      console.error(error);
    }

    this.saving = false;
  }
}
