import { type ReactNode, useEffect, useRef, useCallback } from 'react';
import { type NativeEventSubscription, AppState, type AppStateStatus } from 'react-native';
import dayJs from 'dayjs';
import { setItem, getItem } from '../components/utlis/storageHelper';
import CosmosForegroundService, {
  type IForegroundTask,
} from '../services/foregroundServices/foreground.service';
import useIsOnline from '../hooks/useIsOnline';
import {
  IGeolocationPayload,
  getSyncTime,
  sendLocationAndActivenessToServer,
} from '../hooks/capturingApi';
import { isTimeDifferenceWithinRange } from '../components/utlis/commonFunctions';
import { setDeviceGeolocationsBuffer, setIsTimeSynced } from '../reducer/foregroundServiceSlice';
import { CaptureGeolocation } from '../components/form/services/geoLocation.service';
import { logError } from '../components/utlis/errorUtils';
import { useAppDispatch, useAppSelector } from '../hooks';
import { dataSyncService } from '../services/dataSync.service';
import { DATA_SYNC_TIME_INTERVAL, IS_DATA_SYNC_REQUIRED } from '../constants/config';
import useIsLocationEnabled from '../hooks/useIsLocationEnabled';
import {
  type ISyncCaseIdPayload,
  type ISyncedCases,
  SyncStatus,
  fetchCasesToSync,
  getCasesSyncStatus,
  sendSyncCaseIds,
} from '../action/firebaseFallbackActions';
import { getSyncCaseIds } from '../components/utlis/firebaseFallbackUtils';
import { syncCasesByFallback } from '../reducer/allCasesSlice';
import { MILLISECONDS_IN_A_MINUTE } from '../../RN-UI-LIB/src/utlis/common';
import { setLockData } from '../reducer/userSlice';
import { getConfigData } from '../action/configActions';
import { AppStates } from '../types/appStates';
import { StorageKeys } from '../types/storageKeys';
import { AgentActivity } from '../types/agentActivity';
import {
  getActivityTimeOnApp,
  getActivityTimeWindowMedium,
  getActivityTimeWindowHigh,
} from './AgentActivityConfigurableConstants';
import RNFS from 'react-native-fs';
import { GlobalImageMap } from './CachedImage';
import { get } from 'react-hook-form';
import { addClickstreamEvent } from '../services/clickstreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from './Constants';

export enum FOREGROUND_TASKS {
  GEOLOCATION = 'GEOLOCATION',
  TIME_SYNC = 'TIME_SYNC',
  DATA_SYNC = 'DATA_SYNC',
  FIRESTORE_FALLBACK = 'FIRESTORE_FALLBACK',
  UPDATE_AGENT_ACTIVENESS = 'UPDATE_AGENT_ACTIVENESS',
  UPDATE_AGENT_ACTIVITY = 'UPDATE_AGENT_ACTIVITY',
  DELETE_CACHE = 'DELETE_CACHE',
}

interface ITrackingComponent {
  children?: ReactNode;
}

let LAST_SYNC_STATUS = 'SKIP';
const ACTIVITY_TIME_WINDOW = 10; // 10 minutes

const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
  const isOnline = useIsOnline();
  const dispatch = useAppDispatch();
  const appState = useRef(AppState.currentState);
  const isTeamLead = useAppSelector((state) => state.user.isTeamLead);

  const {
    referenceId,
    pendingList = [],
    pinnedList = [],
    geolocations = [],
  } = useAppSelector((state) => ({
    referenceId: state.user.user?.referenceId!,
    pendingList: state.allCases.pendingList,
    pinnedList: state.allCases.pinnedList,
    geolocations: state.foregroundService.deviceGeolocationsBuffer,
  }));

  const handleTimeSync = async () => {
    try {
      if (!isOnline) {
        return;
      }
      const timestamp = await getSyncTime();
      if (timestamp) {
        const isTimeDifferenceLess = isTimeDifferenceWithinRange(timestamp, 5);
        dispatch(setIsTimeSynced(isTimeDifferenceLess));
      }
    } catch (e: any) {
      logError(e, 'Error during fetching timestamp from server.');
    }
  };

  const handleSendGeolocation = async () => {
    try {
      const location = await CaptureGeolocation.fetchLocation(Date.now() + '', 0, appState.current);
      if (!location) {
        return;
      }
      const isActiveOnApp: string | boolean = (await getItem(StorageKeys.IS_USER_ACTIVE)) || false;
      const userActivityonApp: string =
        (await getItem(StorageKeys.USER_ACTIVITY_ON_APP)) || AgentActivity.LOW;

      const geolocation: IGeolocationPayload = {
        latitude: location.latitude,
        longitude: location.longitude,
        accuracy: location.accuracy,
        timestamp: Date.now(),
        isActiveOnApp: Boolean(isActiveOnApp),
        userActivityOnApp: String(userActivityonApp),
      };
      dispatch(setDeviceGeolocationsBuffer(geolocation));
      dispatch(sendLocationAndActivenessToServer([geolocation]));
    } catch (e: any) {
      logError(e, 'Error during background location sending.');
    }
  };

  useEffect(() => {
    if (!isOnline) {
      return;
    }
    if (geolocations.length) {
      dispatch(sendLocationAndActivenessToServer(geolocations, true));
    }
  }, [geolocations, isOnline]);

  const handleGetCaseSyncStatus = async () => {
    try {
      if (!isOnline) {
        return;
      }
      const { syncStatus, visitPlanStatus } = (await getCasesSyncStatus(referenceId)) ?? {};
      if (syncStatus) {
        // Keep track of the last status received
        LAST_SYNC_STATUS = syncStatus;
      }
      if (syncStatus === SyncStatus.SEND_CASES) {
        const cases = getSyncCaseIds([...pendingList, ...pinnedList]);
        const payload: ISyncCaseIdPayload = {
          agentId: referenceId,
          cases,
        };
        sendSyncCaseIds(payload);
      } else if (syncStatus === SyncStatus.FETCH_CASES) {
        const updatedDetails: ISyncedCases = await fetchCasesToSync(referenceId);
        if (updatedDetails?.cases?.length) {
          dispatch(syncCasesByFallback(updatedDetails));
        }
      }
      if (visitPlanStatus) {
        dispatch(
          setLockData({
            visitPlanStatus,
          })
        );
      }
    } catch (e) {
      logError(e as Error, 'Error during fetching case sync status');
    }
  };

  const handleUpdateActiveness = async () => {
    if (AppState.currentState === AppStates.ACTIVE) {
      await setItem(StorageKeys.IS_USER_ACTIVE, 'true');
      return;
    }
    const foregroundTimestamp = await getItem(StorageKeys.APP_FOREGROUND_TIMESTAMP);
    const backgroundTimestamp = await getItem(StorageKeys.APP_BACKGROUND_TIMESTAMP);
    const foregroundTime = dayJs(foregroundTimestamp);
    const backgroundTime = dayJs(backgroundTimestamp);
    const diffBetweenBackgroundAndForegroundTime = dayJs(backgroundTime).diff(
      foregroundTime,
      'seconds'
    );
    const diffBetweenCurrentTimeAndForegroundTime =
      dayJs().diff(foregroundTime, 'minutes') < 0 ? 0 : dayJs().diff(foregroundTime, 'minutes');
    const isForegroundTimeWithInRange =
      diffBetweenCurrentTimeAndForegroundTime <= ACTIVITY_TIME_WINDOW;
    const isForegroundTimeAfterBackground = dayJs(foregroundTimestamp).isAfter(backgroundTimestamp);
    const ACTIVITY_TIME_ON_APP = getActivityTimeOnApp();

    if (isForegroundTimeWithInRange) {
      if (
        isForegroundTimeAfterBackground ||
        diffBetweenBackgroundAndForegroundTime >= ACTIVITY_TIME_ON_APP
      ) {
        await setItem(StorageKeys.IS_USER_ACTIVE, 'true');
        return;
      }
      await setItem(StorageKeys.IS_USER_ACTIVE, 'false');
    }
    await setItem(StorageKeys.IS_USER_ACTIVE, 'false');
    return;
  };

  const handleUpdateActivity = async () => {
    const foregroundTimestamp = await getItem(StorageKeys.APP_FOREGROUND_TIMESTAMP);
    const backgroundTimestamp = await getItem(StorageKeys.APP_BACKGROUND_TIMESTAMP);
    const stateSetTimestamp = await getItem(StorageKeys.STATE_SET_TIMESTAMP);

    if (foregroundTimestamp == null) {
      console.log('fts set after installation');
      await setItem(StorageKeys.APP_FOREGROUND_TIMESTAMP, dayJs().toString());
    }

    const foregroundTime = dayJs(foregroundTimestamp);
    const backgroundTime = dayJs(backgroundTimestamp);
    const stateSetTime = dayJs(stateSetTimestamp);

    const diffBetweenCurrentTimeAndForegroundTime =
      dayJs().diff(foregroundTime, 'seconds') < 0 ? 0 : dayJs().diff(foregroundTime, 'seconds');
    const diffBetweenCurrentTimeAndSetStateTime =
      dayJs().diff(stateSetTime, 'minutes') < 0 ? 0 : dayJs().diff(stateSetTime, 'minutes');

    const ACTIVITY_TIME_ON_APP = getActivityTimeOnApp();
    const ACTIVITY_TIME_WINDOW_HIGH = getActivityTimeWindowHigh();
    const ACTIVITY_TIME_WINDOW_MEDIUM = getActivityTimeWindowMedium();

    const isStateSetTimeWithinHighRange =
      diffBetweenCurrentTimeAndSetStateTime < ACTIVITY_TIME_WINDOW_HIGH;
    const isStateSetTimeWithinMediumRange =
      diffBetweenCurrentTimeAndSetStateTime < ACTIVITY_TIME_WINDOW_MEDIUM;
    const isForegroundTimeAfterBackground = dayJs(foregroundTimestamp).isAfter(backgroundTimestamp);

    if (AppState.currentState === AppStates.ACTIVE) {
      if (diffBetweenCurrentTimeAndForegroundTime >= ACTIVITY_TIME_ON_APP) {
        await setItem(StorageKeys.USER_ACTIVITY_ON_APP, AgentActivity.HIGH);
        return;
      }
      return;
    }

    if (isForegroundTimeAfterBackground) {
      if (diffBetweenCurrentTimeAndForegroundTime >= ACTIVITY_TIME_ON_APP) {
        await setItem(StorageKeys.USER_ACTIVITY_ON_APP, AgentActivity.HIGH);
        return;
      }
      return;
    } else if (isStateSetTimeWithinHighRange) {
      return;
    } else if (isStateSetTimeWithinMediumRange) {
      await setItem(StorageKeys.USER_ACTIVITY_ON_APP, AgentActivity.MEDIUM);
      return;
    } else {
      await setItem(StorageKeys.USER_ACTIVITY_ON_APP, AgentActivity.LOW);
      return;
    }
  };

  const deleteCache = () => {
    const directoryPath = RNFS.CachesDirectoryPath;
    const currentDate = new Date().getTime();

    RNFS.readdir(directoryPath)
      .then((files) => {
        for (const file of files) {
          const filePath = `${directoryPath}/${file}`;
          if (!file.endsWith('jpg') || !file.endsWith('pdf')) {
            continue;
          }
          RNFS.stat(filePath)
            .then((fileStat) => {
              // Calculate the age of the file in milliseconds
              const fileAgeMs = currentDate - new Date(fileStat.mtime).getTime();

              // Check if the file is older than 30 days (30 days = 30 * 24 * 60 * 60 * 1000 milliseconds)

              if (fileAgeMs > 30 * 24 * 60 * 60 * 1000) {
                delete GlobalImageMap[filePath];
                return RNFS.unlink(filePath); // Delete the file
              }
            })
            .then(() => {
              console.log(`Deleted old file: ${file}`);
            })
            .catch((error) => {
              console.error(`Error deleting file: ${file}`, error);
            });
        }
      })
      .catch((error) => {
        console.error('Error reading directory:', error);
      });
  };

  const tasks: IForegroundTask[] = [
    {
      taskId: FOREGROUND_TASKS.TIME_SYNC,
      task: handleTimeSync,
      delay: 5 * MILLISECONDS_IN_A_MINUTE, // 5 minutes,
      onLoop: true,
    },
    {
      taskId: FOREGROUND_TASKS.GEOLOCATION,
      task: handleSendGeolocation,
      delay: 3 * MILLISECONDS_IN_A_MINUTE, // 3 minutes
      onLoop: true,
    },
    {
      taskId: FOREGROUND_TASKS.UPDATE_AGENT_ACTIVENESS,
      task: handleUpdateActiveness,
      delay: ACTIVITY_TIME_WINDOW * MILLISECONDS_IN_A_MINUTE, // 10 minutes
      onLoop: true,
    },
    {
      taskId: FOREGROUND_TASKS.UPDATE_AGENT_ACTIVITY,
      task: handleUpdateActivity,
      delay: ACTIVITY_TIME_WINDOW * MILLISECONDS_IN_A_MINUTE, // 10 minutes
      onLoop: true,
    },
    {
      taskId: FOREGROUND_TASKS.DELETE_CACHE,
      task: deleteCache,
      delay: DATA_SYNC_TIME_INTERVAL,
      onLoop: true,
    },
  ];

  if (!isTeamLead) {
    tasks.push({
      taskId: FOREGROUND_TASKS.FIRESTORE_FALLBACK,
      task: handleGetCaseSyncStatus,
      delay: 5 * MILLISECONDS_IN_A_MINUTE, // 5 minutes
      onLoop: true,
    });
  }

  const handleDataSync = () => {
    if (!isOnline) {
      return;
    }
    dataSyncService();
  };

  if (IS_DATA_SYNC_REQUIRED) {
    tasks.push({
      taskId: FOREGROUND_TASKS.DATA_SYNC,
      task: handleDataSync,
      delay: DATA_SYNC_TIME_INTERVAL,
      onLoop: true,
    });
  }

  const userActivityUpdateOnBackground = async () => {
    const foregroundTimestamp = await getItem(StorageKeys.APP_FOREGROUND_TIMESTAMP);
    const backgroundTimestamp = await getItem(StorageKeys.APP_BACKGROUND_TIMESTAMP);
    const foregroundTime = dayJs(foregroundTimestamp);
    const backgroundTime = dayJs(backgroundTimestamp);
    const diffBetweenBackgroundAndForegroundTime = dayJs(backgroundTime).diff(
      foregroundTime,
      'seconds'
    );
    const ACTIVITY_TIME_ON_APP = getActivityTimeOnApp();

    if (diffBetweenBackgroundAndForegroundTime >= ACTIVITY_TIME_ON_APP) {
      await setItem(StorageKeys.USER_ACTIVITY_ON_APP, AgentActivity.HIGH);
      await setItem(StorageKeys.STATE_SET_TIMESTAMP, dayJs().toString());
      return;
    }
    return;
  };

  const handleAppStateChange = async (nextAppState: AppStateStatus) => {
    // App comes to foreground from background
    const now = dayJs().toString();
    if (nextAppState === AppStates.ACTIVE) {
      await setItem(StorageKeys.APP_FOREGROUND_TIMESTAMP, now);
      addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_APP_FOREGROUND, { now });
      handleGetCaseSyncStatus();
      dispatch(getConfigData());
      CosmosForegroundService.start(tasks);
    }
    if (nextAppState === AppStates.BACKGROUND) {
      await setItem(StorageKeys.APP_BACKGROUND_TIMESTAMP, now);
      userActivityUpdateOnBackground();
      addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_APP_BACKGROUND, { now });
    }
    appState.current = nextAppState;
  };

  // Fetch cases on login initially and set data
  useEffect(() => {
    (async () => {
      if (!referenceId) {
        return;
      }
      await handleGetCaseSyncStatus();
      dispatch(getConfigData());
      if (!isTeamLead && LAST_SYNC_STATUS !== SyncStatus.FETCH_CASES) {
        const updatedDetails: ISyncedCases = await fetchCasesToSync(referenceId);
        if (updatedDetails?.cases?.length) {
          dispatch(syncCasesByFallback(updatedDetails));
        }
      }
    })();
  }, []);

  useEffect(() => {
    let appStateSubscription: NativeEventSubscription;
    appStateSubscription = AppState.addEventListener('change', handleAppStateChange);
    CosmosForegroundService.start(tasks);
    return () => {
      appStateSubscription?.remove();
    };
  }, []);

  useIsLocationEnabled();

  return <>{children}</>;
};

export default TrackingComponent;
