import { useCallback, useEffect, useRef, useState } from 'react';
import { useMutation, useQuery } from 'react-query';

import { type AxiosError, type AxiosResponse } from 'axios';

import useHuSnackbar from '@material-hu/components/design-system/Snackbar';

import { logEvent } from 'src/config/amplitude';
import { queryClient } from 'src/config/react-query';
import { useAuth } from 'src/contexts/JWTContext';
import { useGetUserSites } from 'src/hooks/queryHooks/timeTracking';
import useFeatureFlag from 'src/hooks/useFeatureFlag';
import useGeneralError from 'src/hooks/useGeneralError';
import { useTimeTrackingAccess } from 'src/hooks/useTimeTrackingAccess';
import {
  invalidateControlRegistersQueries,
  timeTrackingKeys,
} from 'src/pages/dashboard/timeTracking/queries';
import {
  type ClockInParams,
  getTimeTrackingDaySummary,
  persistClockIn,
  persistClockInDeagle,
} from 'src/services/timeTracking';
import { EventName } from 'src/types/amplitude';
import { FeatureFlags } from 'src/types/featureFlags';
import { type ResponseError } from 'src/types/services';
import {
  type ClockSummaries,
  EntrySource,
  EntryType,
  type TimeTrackingEntry,
} from 'src/types/timeTracking';
import { useLokaliseTranslation } from 'src/utils/i18n';
import { pairEntries } from 'src/utils/timeTracking';

import { useChronometer } from './useChronometer';

const FIVE_MINUTES = 5 * 60 * 1000;

export const useClockingInfo = () => {
  const [isClockRunning, setIsClockRunning] = useState(false);
  const { t } = useLokaliseTranslation('time_tracker');
  const { enqueueSnackbar } = useHuSnackbar();
  const lastEntryStartRef = useRef<Partial<TimeTrackingEntry>>();
  const showGeneralError = useGeneralError();
  const { user } = useAuth();
  const { canAccessClock: enableClock, policies: timeTrackingPolicies } =
    useTimeTrackingAccess();

  const useDeagleEndpoint = useFeatureFlag(
    FeatureFlags.TIME_TRACKING_HIGH_AVAILABILITY,
  );

  const { sites, isFetching: isFetchingSites } = useGetUserSites(
    enableClock ? user?.id! : 0,
  );

  const { data: clockingInfo, isFetching: isFetchingClockingInfo } = useQuery(
    timeTrackingKeys.daySummary(),
    () => getTimeTrackingDaySummary(),
    {
      enabled: enableClock,
      select: res => res.data,
      staleTime: FIVE_MINUTES,
    },
  );

  const { hours, minutes, seconds } = useChronometer({
    workedHours: 0,
    lastStartEntry: lastEntryStartRef.current?.time ?? undefined, // Last entry if theres start and no end
    runningClock: isClockRunning,
  });

  const { mutate: toggleClockMutation, isLoading: isLoadingToggleClock } =
    useMutation({
      mutationFn: (params: ClockInParams) =>
        useDeagleEndpoint
          ? persistClockInDeagle(params)
          : persistClockIn(params),
      onSuccess: (response, variables) => {
        logEvent(EventName.TIME_TRACKING_CLOCK_STARTED, {
          type: variables.type,
          location: variables.siteId,
          source: EntrySource.WEB,
        });
        enqueueSnackbar({
          title:
            variables.type === EntryType.END
              ? t('toggle_clock_success_end')
              : t('toggle_clock_success_start'),
          ...(variables.type !== EntryType.END && {
            description: t('toggle_clock_success_description'),
          }),
          variant: 'success',
        });
        const cachedDaySummary = queryClient.getQueryData<
          AxiosResponse<ClockSummaries> | undefined
        >(timeTrackingKeys.daySummary());
        if (!cachedDaySummary) {
          // Fetch failed earlier and cache is empty — refetch from server instead of optimistic update.
          queryClient.invalidateQueries(timeTrackingKeys.daySummary());
        } else {
          const [newestEntry, ...restOfEntries] = cachedDaySummary.data.entries;
          queryClient.setQueryData<AxiosResponse<ClockSummaries>>(
            timeTrackingKeys.daySummary(),
            {
              ...cachedDaySummary,
              data: {
                ...cachedDaySummary.data,
                entries:
                  variables.type === EntryType.END
                    ? [{ ...newestEntry, end: response.data }, ...restOfEntries]
                    : [
                        { start: response.data },
                        ...cachedDaySummary.data.entries,
                      ],
              },
            },
          );
        }
        invalidateControlRegistersQueries();
      },
      onError: (error: AxiosError<ResponseError>, variables) => {
        // May happen if user is marking using 2 tabs/browsers/devices
        if (error.response?.data?.code === 'TIME_TRACKING_ENTRY_DUPLICATE') {
          queryClient.invalidateQueries(timeTrackingKeys.daySummary());
          enqueueSnackbar({
            title:
              variables.type === EntryType.END
                ? t('toggle_clock_success_end')
                : t('toggle_clock_success_start'),
            ...(variables.type !== EntryType.END && {
              description: t('toggle_clock_success_description', {
                context: variables.type,
              }),
            }),
            variant: 'success',
          });
          return;
        }
        showGeneralError({
          title: t('register_error_title'),
          description: t('register_error_description'),
        });
      },
    });

  useEffect(() => {
    const currentDaySummaryEntries = clockingInfo?.entries;
    const previousDaySummaryEntries =
      clockingInfo?.previousDaySummary?.timeTrackingEntries;
    let clockIsRunning = false;

    if (currentDaySummaryEntries?.length) {
      if (currentDaySummaryEntries[0].start) {
        const isDayChangeOfNightShift =
          clockingInfo?.daySummary?.nightShift &&
          currentDaySummaryEntries[0].start.source === EntrySource.DAY_CHANGE;
        const onlyStartEntry =
          !!clockingInfo?.entries[0]?.start && !clockingInfo?.entries[0]?.end;
        clockIsRunning = onlyStartEntry;
        // When nightshift is enabled day changes should not be taken into account for last start entries
        if (!isDayChangeOfNightShift) {
          lastEntryStartRef.current = currentDaySummaryEntries[0]?.start;
        }
      }
    } else if (previousDaySummaryEntries?.length) {
      const lastEntryPair = pairEntries(previousDaySummaryEntries)[0];
      const onlyStartEntry = !!lastEntryPair.start && !lastEntryPair.end;
      clockIsRunning = onlyStartEntry;
      lastEntryStartRef.current = lastEntryPair.start;
    }
    setIsClockRunning(clockIsRunning);
  }, [clockingInfo]);

  const toggleClock = useCallback(
    (params: ClockInParams) => {
      // Warm the cache opportunistically so onSuccess can do an optimistic update.
      // Fire both concurrently — if the prefetch wins, onSuccess uses the cached value;
      // if the mutation wins first, the invalidateQueries fallback in onSuccess kicks in.
      queryClient
        .fetchQuery(timeTrackingKeys.daySummary(), getTimeTrackingDaySummary, {
          staleTime: FIVE_MINUTES,
        })
        .catch(() => undefined);
      toggleClockMutation(params);
    },
    [toggleClockMutation],
  );

  return {
    clockTime: {
      hours: `${hours.toString().padStart(2, '0')}h`,
      minutes: `${minutes.toString().padStart(2, '0')}m`,
      seconds: `${seconds.toString().padStart(2, '0')}s`,
    },
    isClockRunning,
    isFetchingClockingInfo,
    isFetchingUserTrackingSites: isFetchingSites,
    isLoadingToggleClock,
    lastEntryStart: lastEntryStartRef.current,
    nightShift: timeTrackingPolicies?.nightShift,
    timeTrackingClockIsEnabled: enableClock,
    toggleClockMutation: toggleClock,
    userTrackingSites: sites,
  };
};

export type UseClockingInfoReturn = ReturnType<typeof useClockingInfo>;
