import { Injectable } from '@angular/core';
import { HotToastService } from '@ngxpert/hot-toast';
import {
  Observable,
  Subject,
  catchError,
  combineLatest,
  map,
  retry,
  shareReplay,
  startWith,
  switchMap,
  tap,
  throwError,
  timer,
} from 'rxjs';
import {
  AccountingFilterArgs,
  AccountingState,
  GetAccountingStateMetaGQL,
  GetAccountingStateMetaQuery,
  GetAccountingToursGQL,
  GetAccountingToursQuery,
} from '../../core/data-access/graphql/generated/generated';

@Injectable({ providedIn: 'root' })
export class DashboardAccountingService {
  toursUpdated$ = timer(60 * 1000).pipe(
    tap((value) => {
      if (value) {
        this.toast.info('Daten wurden aktualisiert', {
          id: 'update',
        });
      }
    }),
    shareReplay(1),
    startWith(undefined)
  );

  updateTours$ = new Subject<void>();

  constructor(
    private readonly getAccountingToursGql: GetAccountingToursGQL,
    private readonly getAccountingStateMetaGql: GetAccountingStateMetaGQL,
    private readonly toast: HotToastService
  ) {
    this.updateTours$.next();
  }

  fetchToursByState(
    state: AccountingState,
    cursor: string,
    filters: AccountingFilterArgs,
    take = 20
  ): Observable<GetAccountingToursQuery['accountingTours']> {
    const ticketsQuery = this.getAccountingToursGql.watch({
      state,
      cursor,
      filters,
      take,
    });

    const ticketsUpdate = combineLatest([
      this.toursUpdated$,
      this.updateTours$,
    ]).pipe(
      switchMap(() => ticketsQuery.refetch()),
      startWith(undefined)
    );

    return combineLatest([ticketsUpdate, ticketsQuery.valueChanges]).pipe(
      catchError((error) => {
        console.error(error);
        return throwError(() => error);
      }),
      retry({ delay: 1000, count: 3 }),
      map(([updatedData, firstData]) =>
        updatedData ? updatedData : firstData
      ),
      map((response) => response.data.accountingTours)
    );
  }

  fetchAccountingStateMeta(
    state: AccountingState
  ): Observable<GetAccountingStateMetaQuery['accountingStateMeta']> {
    const ticketStateMetaQuery = this.getAccountingStateMetaGql.watch({
      state,
    });

    const toursUpdate = combineLatest([
      this.toursUpdated$,
      this.updateTours$,
    ]).pipe(
      switchMap(() => ticketStateMetaQuery.refetch()),
      startWith(undefined)
    );

    return combineLatest([toursUpdate, ticketStateMetaQuery.valueChanges]).pipe(
      catchError((error) => {
        console.error(error);
        return throwError(() => error);
      }),
      retry({ delay: 1000, count: 3 }),
      map(([updatedData, firstData]) =>
        updatedData ? updatedData : firstData
      ),
      map((response) => response.data.accountingStateMeta)
    );
  }
}
