import { Injectable } from '@angular/core';
import { HotToastService } from '@ngxpert/hot-toast';
import {
  BehaviorSubject,
  Observable,
  catchError,
  combineLatest,
  map,
  of,
  retry,
  shareReplay,
  startWith,
  switchMap,
  tap,
  throwError,
} from 'rxjs';
import {
  GetTimePerTourMetricForDayGQL,
  GetTimePerTourMetricForDayQuery,
  GetTimePerTourMetricForMonthGQL,
  GetTimePerTourMetricForMonthQuery,
  GetTourAmountPerTimeKpiManagerGQL,
  GetTourAmountPerTimeKpiManagerQuery,
  ManagerKpiGQL,
  ManagerKpiQuery,
  TimePerTourUpdatedGQL,
} from '../../core/data-access/graphql/generated/generated';

@Injectable({ providedIn: 'root' })
export class ManagerDashboardService {
  isLoadingDayview$ = new BehaviorSubject<boolean>(false);
  isLoadingMonthview$ = new BehaviorSubject<boolean>(false);
  timePerTourUpdate$;

  constructor(
    private readonly getTourAmountPerTimeKPIGql: GetTourAmountPerTimeKpiManagerGQL,
    private readonly getTimePerTourMetricForDayGql: GetTimePerTourMetricForDayGQL,
    private readonly getTimePerTourMetricForMonthGql: GetTimePerTourMetricForMonthGQL,
    private readonly timePerTourUpdatedGql: TimePerTourUpdatedGQL,
    private readonly managerKpiGql: ManagerKpiGQL,
    private readonly toast: HotToastService
  ) {
    this.timePerTourUpdate$ = this.timePerTourUpdatedGql.subscribe().pipe(
      map((res) => res.data?.timePerTourUpdated),
      tap((value) => {
        if (value) {
          this.toast.info('Daten wurden aktualisiert', {
            id: 'update',
          });
        }
      }),
      shareReplay(1)
    );
  }

  fetchManagerKpi(
    ordinanceType: string
  ): Observable<ManagerKpiQuery['managerKpi']> {
    const managerKpiQuery = this.managerKpiGql.watch({
      ordinanceType,
    });

    const update = this.timePerTourUpdate$.pipe(
      switchMap(() => managerKpiQuery.refetch()),
      startWith(undefined)
    );

    return combineLatest([update, managerKpiQuery.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.managerKpi)
    );
  }

  getTimePerTourKPIByOrdinanceTypes(
    ordinanceType: string
  ): Observable<GetTourAmountPerTimeKpiManagerQuery['toursPerTimeKPI']> {
    const timePerTourKPIQuery = this.getTourAmountPerTimeKPIGql.watch({
      ordinanceType,
    });

    const timePerTourKPIUpdate = this.timePerTourUpdate$.pipe(
      switchMap(() => timePerTourKPIQuery.refetch()),
      startWith(undefined)
    );
    return combineLatest([
      timePerTourKPIUpdate,
      timePerTourKPIQuery.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.toursPerTimeKPI)
    );
  }

  getTimePerTourMetricByOrdinanceTypeForDay(
    ordinanceType: string,
    day: Date
  ): Observable<GetTimePerTourMetricForDayQuery['timePerTourMetricForDay']> {
    const timePerTourMetricQuery = this.getTimePerTourMetricForDayGql.watch({
      ordinanceType,
      day,
    });

    const timePerTourKPIUpdate = this.timePerTourUpdate$.pipe(
      switchMap(() => timePerTourMetricQuery.refetch()),
      startWith(undefined)
    );
    return combineLatest([
      timePerTourKPIUpdate,
      of([]).pipe(
        tap(() => this.isLoadingDayview$.next(true)),
        switchMap(() => timePerTourMetricQuery.valueChanges)
      ),
    ]).pipe(
      catchError((error) => {
        console.error(error);
        return throwError(() => error);
      }),
      retry({ delay: 1000, count: 3 }),
      tap(() => this.isLoadingDayview$.next(false)),
      map(([updatedData, firstData]) =>
        updatedData ? updatedData : firstData
      ),
      map((response) => response.data.timePerTourMetricForDay)
    );
  }

  getTimePerTourMetricByOrdinanceTypeForMonth(
    ordinanceType: string,
    day: Date
  ): Observable<
    GetTimePerTourMetricForMonthQuery['timePerTourMetricForMonth']
  > {
    const timePerTourMetricQuery = this.getTimePerTourMetricForMonthGql.watch(
      {
        ordinanceType,
        day,
      },
      { errorPolicy: 'all' }
    );

    const timePerTourKPIUpdate = this.timePerTourUpdate$.pipe(
      switchMap(() => timePerTourMetricQuery.refetch()),
      startWith(undefined)
    );
    return combineLatest([
      timePerTourKPIUpdate,
      of([]).pipe(
        tap(() => this.isLoadingMonthview$.next(true)),
        switchMap(() => timePerTourMetricQuery.valueChanges)
      ),
    ]).pipe(
      catchError((error) => {
        console.error(error);
        return throwError(() => error);
      }),
      retry({ delay: 10000, count: 3 }),
      tap(() => this.isLoadingMonthview$.next(false)),
      map(([updatedData, firstData]) =>
        updatedData ? updatedData : firstData
      ),
      map((response) => {
        if (response.error || response.errors) {
          console.error(response.error, response.errors);
        }
        return response.data.timePerTourMetricForMonth;
      })
    );
  }
}
