import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { ApplicationService } from '@core/application';
import { BreadcrumbService } from '@core/breadcrumb';
import { DomainAvailabilityService, ItemData } from '@domain/availability';
import { DomainCatalogService } from '@domain/catalog';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { ItemDTO, ResponseArgsOfItemDTO } from '@swagger/cat';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
import { combineLatest, Observable, of } from 'rxjs';
import { catchError, filter, first, map, shareReplay, switchMap, tap } from 'rxjs/operators';

export interface ArticleDetailsState {
  fetchingItem?: boolean;
  fetchingItemError?: string;
  item?: ItemDTO;

  fetchingPromotionPoints?: boolean;
  fetchingPromotionPointsError?: string;

  fetchingTakeAwayAvailability?: boolean;
  fetchingTakeAwayAvailabilityError?: string;

  fetchingPickUpAvailability?: boolean;
  fetchingPickUpAvailabilityError?: string;

  fetchingDownloadAvailability?: boolean;
  fetchingDownloadAvailabilityError?: string;

  fetchingDeliveryAvailability?: boolean;
  fetchingDeliveryAvailabilityError?: string;

  fetchingDeliveryDigAvailability?: boolean;
  fetchingDeliveryDigAvailabilityError?: string;

  fetchingDeliveryB2BAvailability?: boolean;
  fetchingDeliveryB2BAvailabilityError?: string;
}
@Injectable()
export class ArticleDetailsStore extends ComponentStore<ArticleDetailsState> {
  //#region Artikel
  readonly fetchingItem$ = this.select((s) => s.fetchingItem);
  readonly item$ = this.select((s) => s.item);
  readonly itemData$ = this.select(this.item$, (item) =>
    !!item ? ({ ean: item?.product?.ean, itemId: item?.id, price: item?.catalogAvailability?.price } as ItemData) : undefined
  );
  //#endregion

  readonly isDownload$ = this.select(this.item$, (item) => !!(item?.product?.format === 'EB' || item?.product?.format === 'DL'));

  readonly branch$ = this.domainAvailabilityService.getCurrentBranch();

  readonly reviewRating$ = this.item$.pipe(
    filter((i) => !!i),
    map((i) => (i.reviews?.length > 0 ? i.reviews.map((r) => r.rating).reduce((total, num) => total + num) / i.reviews.length : 0))
  );

  readonly recommendations$ = this.item$.pipe(
    filter((item) => !!item),
    switchMap((item) =>
      item.ids?.dig ? this.domainCatalogService.getRecommendations({ digId: item.ids['dig'] }).pipe(map((res) => res.result)) : of([])
    )
  );

  //#region Lesepunkte
  readonly fetchingPromotionPoints$ = this.select((s) => s.fetchingPromotionPoints);
  readonly promotionPoints$ = this.item$.pipe(
    filter((item) => !!item?.id && !!item.catalogAvailability?.price?.value?.value),
    tap(() => this.patchState({ fetchingPromotionPoints: true, fetchingPromotionPointsError: undefined })),
    switchMap((item) =>
      !!item
        ? this.domainCatalogService
            .getPromotionPoints({
              items: [{ id: item.id, quantity: 1, price: item.catalogAvailability.price.value.value }],
            })
            .pipe(
              map((res) => res.result[item.id] || 0),
              catchError((err) => {
                console.error('getPromotionPoints failed.', err);
                this.patchState({ fetchingPromotionPointsError: 'Fehler beim laden der Lesepukte.' });
                return of(undefined);
              })
            )
        : of(undefined)
    ),
    tap(() => this.patchState({ fetchingPromotionPoints: false })),
    shareReplay(1)
  );
  //#endregion

  //#region Filialverfügbarkeit
  readonly fetchingTakeAwayAvailability$ = this.select((s) => s.fetchingTakeAwayAvailability);
  readonly takeAwayAvailability$ = combineLatest([this.itemData$, this.isDownload$]).pipe(
    tap(() => this.patchState({ fetchingTakeAwayAvailability: true, fetchingTakeAwayAvailabilityError: undefined })),
    switchMap(([item, isDownload]) =>
      !!item && !isDownload
        ? this.domainAvailabilityService.getTakeAwayAvailability({ item, quantity: 1 }).pipe(
            catchError((err) => {
              console.error('getTakeAwayAvailability failed.', err);
              this.patchState({ fetchingTakeAwayAvailabilityError: 'Fehler beim laden der Filialverfügbarkeit.' });
              return of(undefined);
            })
          )
        : of(undefined)
    ),
    tap(() => this.patchState({ fetchingTakeAwayAvailability: false })),
    shareReplay(1)
  );
  readonly isTakeAwayAvailabilityAvailable$ = this.select(this.takeAwayAvailability$, (availability) =>
    this.domainAvailabilityService.isAvailable({ availability })
  );
  //#endregion

  //#region Abholung
  readonly fetchingPickUpAvailability$ = this.select((s) => s.fetchingPickUpAvailability);
  readonly pickUpAvailability$ = combineLatest([this.itemData$, this.branch$, this.isDownload$]).pipe(
    tap(() => this.patchState({ fetchingPickUpAvailability: true, fetchingPickUpAvailabilityError: undefined })),
    switchMap(([item, branch, isDownload]) =>
      !!item && !!branch && !isDownload
        ? this.domainAvailabilityService.getPickUpAvailability({ item, branch, quantity: 1 }).pipe(
            catchError((err) => {
              console.error('getPickUpAvailability failed.', err);
              this.patchState({ fetchingPickUpAvailabilityError: 'Fehler beim laden der Abholung.' });
              return of(undefined);
            })
          )
        : of(undefined)
    ),
    tap(() => this.patchState({ fetchingPickUpAvailability: false })),
    shareReplay(1)
  );
  readonly isPickUpAvailabilityAvailable$ = this.select(this.pickUpAvailability$, (availability) =>
    this.domainAvailabilityService.isAvailable({ availability })
  );
  //#endregion

  //#region Download
  readonly fetchingDownloadAvailability$ = this.select((s) => s.fetchingDownloadAvailability);
  readonly downloadAvailability$ = combineLatest([this.itemData$, this.isDownload$]).pipe(
    tap(() => this.patchState({ fetchingDownloadAvailability: true, fetchingDownloadAvailabilityError: undefined })),
    switchMap(([item, isDownload]) =>
      !!item && !!isDownload
        ? this.domainAvailabilityService.getDownloadAvailability({ item }).pipe(
            catchError((err) => {
              console.error('getDownloadAvailability failed', err);
              this.patchState({ fetchingDownloadAvailabilityError: 'Fehler beim Laden des Downloads' });
              return of(undefined);
            })
          )
        : of(undefined)
    ),
    tap(() => this.patchState({ fetchingDownloadAvailability: false })),
    shareReplay(1)
  );
  readonly isDownloadAvailabilityAvailable$ = this.select(this.downloadAvailability$, (availability) =>
    this.domainAvailabilityService.isAvailable({ availability })
  );
  //#endregion

  //#region Versandverfügbarkeit
  readonly fetchingDeliveryAvailability$ = this.select((s) => s.fetchingDeliveryAvailability);
  readonly deliveryAvailability$ = combineLatest([this.itemData$, this.isDownload$]).pipe(
    tap(() => this.patchState({ fetchingDeliveryAvailability: true, fetchingDeliveryAvailabilityError: undefined })),
    switchMap(([item, isDownload]) =>
      !!item && !isDownload
        ? this.domainAvailabilityService.getDeliveryAvailability({ item, quantity: 1 }).pipe(
            catchError((err) => {
              console.error('getDeliveryAvailability failed', err);
              this.patchState({ fetchingDeliveryAvailabilityError: 'Fehler beim Laden der Versandbestellung' });
              return of(undefined);
            })
          )
        : of(undefined)
    ),
    tap(() => this.patchState({ fetchingDeliveryAvailability: false })),
    shareReplay(1)
  );
  readonly isDeliveryAvailabilityAvailable$ = this.select(this.deliveryAvailability$, (availability) =>
    this.domainAvailabilityService.isAvailable({ availability })
  );
  //#endregion

  //#region DIG Versandverfügbarkeit
  readonly fetchingDeliveryDigAvailability$ = this.select((s) => s.fetchingDeliveryDigAvailability);
  readonly deliveryDigAvailability$ = combineLatest([this.itemData$, this.isDownload$]).pipe(
    tap(() => this.patchState({ fetchingDeliveryDigAvailability: true, fetchingDeliveryDigAvailabilityError: undefined })),
    switchMap(([item, isDownload]) =>
      !!item && !isDownload
        ? this.domainAvailabilityService.getDigDeliveryAvailability({ item, quantity: 1 }).pipe(
            catchError((err) => {
              console.error('getDigDeliveryAvailability failed', err);
              this.patchState({ fetchingDeliveryDigAvailabilityError: 'Fehler beim Laden der DIG-Versandbestellung' });
              return of(undefined);
            })
          )
        : of(undefined)
    ),
    tap(() => this.patchState({ fetchingDeliveryDigAvailability: false })),
    shareReplay(1)
  );
  readonly isDeliveryDigAvailabilityAvailable$ = this.select(this.deliveryDigAvailability$, (availability) =>
    this.domainAvailabilityService.isAvailable({ availability })
  );
  //#endregion

  //#region B2B Versandverfügbarkeit
  readonly fetchingDeliveryB2BAvailability$ = this.select((s) => s.fetchingDeliveryB2BAvailability);
  readonly deliveryB2BAvailability$ = combineLatest([this.itemData$, this.isDownload$]).pipe(
    tap(() => this.patchState({ fetchingDeliveryB2BAvailability: true, fetchingDeliveryB2BAvailabilityError: undefined })),
    switchMap(([item, isDownload]) =>
      !!item && !isDownload
        ? this.domainAvailabilityService.getB2bDeliveryAvailability({ item, quantity: 1 }).pipe(
            catchError((err) => {
              console.error('getB2BDeliveryAvailability failed', err);
              this.patchState({ fetchingDeliveryB2BAvailabilityError: 'Fehler beim Laden der B2B-Versandbestellung' });
              return of(undefined);
            })
          )
        : of(undefined)
    ),
    tap(() => this.patchState({ fetchingDeliveryB2BAvailability: false })),
    shareReplay(1)
  );
  readonly isDeliveryB2BAvailabilityAvailable$ = this.select(this.deliveryB2BAvailability$, (availability) =>
    this.domainAvailabilityService.isAvailable({ availability })
  );
  //#endregion

  readonly sscText$ = combineLatest([
    this.item$,
    this.isDownload$,
    this.pickUpAvailability$,
    this.deliveryDigAvailability$,
    this.downloadAvailability$,
  ]).pipe(
    map(([item, isDownload, pickupAvailability, deliveryDigAvailability, downloadAvailability]) => {
      const availability = isDownload ? downloadAvailability : pickupAvailability || deliveryDigAvailability;
      return item?.catalogAvailability?.supplier === 'S' && !isDownload
        ? `${item?.catalogAvailability?.ssc} - ${item?.catalogAvailability?.sscText}`
        : availability?.ssc || availability?.sscText
        ? `${availability?.ssc} - ${availability?.sscText}`
        : 'Keine Lieferanten vorhanden';
    })
  );

  constructor(
    private readonly domainCatalogService: DomainCatalogService,
    private readonly domainAvailabilityService: DomainAvailabilityService,
    private readonly _appService: ApplicationService,
    private readonly _router: Router,
    private readonly _breadcrumb: BreadcrumbService,
    private readonly _modal: UiModalService
  ) {
    super({});
  }

  readonly loadItemById = this.effect((id$: Observable<number>) =>
    id$.pipe(
      filter((id) => !!id),
      tap(() => this.patchState({ fetchingItem: true, item: undefined })),
      switchMap((id) => this.domainCatalogService.getDetailsById({ id })),
      tapResponse<ResponseArgsOfItemDTO>(
        (response) => this.patchState({ item: response.result, fetchingItem: false }),
        async (err) => {
          this.patchState({ item: undefined, fetchingItem: false });
          const errorModalRef = this._modal.open({
            content: UiErrorModalComponent,
            title: 'Fehler beim Laden des Artikels über die ID',
            data: { message: 'Sie kehren nun auf die ursprüngliche Artikeldetailseite zurück' },
          });
          await errorModalRef.afterClosed$.toPromise();
          await this.navigateBack();
        }
      )
    )
  );

  readonly loadItemByEan = this.effect((ean$: Observable<string>) =>
    ean$.pipe(
      filter((ean) => !!ean),
      tap(() => this.patchState({ fetchingItem: true, item: undefined })),
      switchMap((ean) => this.domainCatalogService.getDetailsByEan({ ean })),
      tapResponse<ResponseArgsOfItemDTO>(
        (response) => this.patchState({ item: response.result, fetchingItem: false }),
        async (err) => {
          this.patchState({ item: undefined, fetchingItem: false });
          const errorModalRef = this._modal.open({
            content: UiErrorModalComponent,
            title: 'Fehler beim Laden des Artikels über die EAN',
            data: { message: 'Sie kehren nun auf die ursprüngliche Artikeldetailseite zurück' },
          });
          await errorModalRef.afterClosed$.toPromise();
          await this.navigateBack();
        }
      )
    )
  );

  async navigateBack() {
    const crumb = await this._breadcrumb.getLastActivatedBreadcrumbByKey$(this._appService.activatedProcessId).pipe(first()).toPromise();
    if (crumb) {
      await this._router.navigate([crumb.path]);
    } else {
      await this._router.navigate(['/kunde', this._appService.activatedProcessId, 'product', 'search']);
    }
  }
}
