// debug
import "./wdyr"; // <--- first import
import "./warnFilter";

import "expo-splash-screen";
import BackgroundGeolocation from "react-native-background-geolocation";

import notifee from "@notifee/react-native";
import messaging from "@react-native-firebase/messaging";

import "~/sentry";

import { registerRootComponent } from "expo";

import App from "~/app";

import { onBackgroundEvent as notificationBackgroundEvent } from "~/notifications/onEvent";
import onMessageReceived from "~/notifications/onMessageReceived";

import { createLogger } from "~/lib/logger";
import * as Sentry from "@sentry/react-native";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { STORAGE_KEYS } from "~/storage/storageKeys";

// setup notification, this have to stay in index.js
notifee.onBackgroundEvent(notificationBackgroundEvent);
messaging().setBackgroundMessageHandler(onMessageReceived);

// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in Expo Go or in a native build,
// the environment is set up appropriately
registerRootComponent(App);

// Constants for persistence
const FORCE_SYNC_INTERVAL = 24 * 60 * 60 * 1000;
// const FORCE_SYNC_INTERVAL = 5 * 60 * 1000; // DEBUGGING

// Helper functions for persisting sync time
const getLastSyncTime = async () => {
  try {
    const value = await AsyncStorage.getItem(
      STORAGE_KEYS.GEOLOCATION_LAST_SYNC_TIME,
    );
    return value ? parseInt(value, 10) : Date.now();
  } catch (error) {
    Sentry.captureException(error, {
      tags: { module: "headless-task", operation: "get-last-sync-time" },
    });
    return Date.now();
  }
};

const setLastSyncTime = async (time) => {
  try {
    await AsyncStorage.setItem(
      STORAGE_KEYS.GEOLOCATION_LAST_SYNC_TIME,
      time.toString(),
    );
  } catch (error) {
    Sentry.captureException(error, {
      tags: { module: "headless-task", operation: "set-last-sync-time" },
    });
  }
};

// this have to stay in index.js, see also https://github.com/transistorsoft/react-native-background-geolocation/wiki/Android-Headless-Mode
const getCurrentPosition = () => {
  return new Promise((resolve) => {
    // Add timeout protection
    const timeout = setTimeout(() => {
      resolve({ code: -1, message: "getCurrentPosition timeout" });
    }, 15000); // 15 second timeout

    BackgroundGeolocation.getCurrentPosition(
      {
        samples: 1,
        persist: true,
        extras: { background: true },
        timeout: 10, // 10 second timeout in the plugin itself
      },
      (location) => {
        clearTimeout(timeout);
        resolve(location);
      },
      (error) => {
        clearTimeout(timeout);
        resolve(error);
      },
    );
  });
};

const geolocBgLogger = createLogger({
  service: "background-geolocation",
  task: "headless",
});

const HeadlessTask = async (event) => {
  // Add timeout protection for the entire headless task
  const taskTimeout = setTimeout(() => {
    geolocBgLogger.error("HeadlessTask timeout", { event });

    Sentry.captureException(new Error("HeadlessTask timeout"), {
      tags: {
        module: "background-geolocation",
        operation: "headless-task-timeout",
        eventName: event?.name,
      },
    });
  }, 60000); // 60 second timeout

  // Simple performance tracking without deprecated APIs
  const taskStartTime = Date.now();

  try {
    // Validate event structure
    if (!event || typeof event !== "object") {
      throw new Error("Invalid event object received");
    }

    const { name, params } = event;

    if (!name || typeof name !== "string") {
      throw new Error("Invalid event name received");
    }

    geolocBgLogger.info("HeadlessTask event received", { name, params });

    switch (name) {
      case "heartbeat":
        // Get persisted last sync time
        const lastSyncTime = await getLastSyncTime();
        const now = Date.now();
        const timeSinceLastSync = now - lastSyncTime;

        // Get current position with performance tracking
        const location = await getCurrentPosition();

        geolocBgLogger.debug("getCurrentPosition result", { location });

        if (timeSinceLastSync >= FORCE_SYNC_INTERVAL) {
          geolocBgLogger.info("Forcing location sync after 24h");

          try {
            // Change pace to ensure location updates with timeout
            await Promise.race([
              BackgroundGeolocation.changePace(true),
              new Promise((_, reject) =>
                setTimeout(
                  () => reject(new Error("changePace timeout")),
                  10000,
                ),
              ),
            ]);

            // Perform sync with timeout
            const syncResult = await Promise.race([
              BackgroundGeolocation.sync(),
              new Promise((_, reject) =>
                setTimeout(() => reject(new Error("sync timeout")), 20000),
              ),
            ]);

            // Update last sync time after successful sync
            await setLastSyncTime(now);
          } catch (syncError) {
            Sentry.captureException(syncError, {
              tags: {
                module: "headless-task",
                operation: "force-sync",
                eventName: name,
              },
              contexts: {
                syncAttempt: {
                  timeSinceLastSync: timeSinceLastSync,
                  lastSyncTime: new Date(lastSyncTime).toISOString(),
                },
              },
            });

            geolocBgLogger.error("Force sync failed", { error: syncError });
          }
        }
        break;

      case "location":
        // Validate location parameters
        if (!params || typeof params !== "object") {
          geolocBgLogger.warn("Invalid location params", { params });
          break;
        }

        geolocBgLogger.debug("Location update received", {
          location: params.location,
        });
        break;

      case "http":
        // Validate HTTP parameters
        if (!params || typeof params !== "object" || !params.response) {
          geolocBgLogger.warn("Invalid HTTP params", { params });
          break;
        }

        const httpStatus = params.response?.status;
        const isHttpSuccess = httpStatus === 200;

        geolocBgLogger.debug("HTTP response received", {
          response: params.response,
        });

        // Update last sync time on successful HTTP response
        if (isHttpSuccess) {
          try {
            const now = Date.now();
            await setLastSyncTime(now);
          } catch (syncTimeError) {
            geolocBgLogger.error("Failed to update sync time", {
              error: syncTimeError,
            });

            Sentry.captureException(syncTimeError, {
              tags: {
                module: "headless-task",
                operation: "update-sync-time-http",
              },
            });
          }
        }
        break;

      default:
        break;
    }

    // Task completed successfully
    const taskDuration = Date.now() - taskStartTime;
  } catch (error) {
    const taskDuration = Date.now() - taskStartTime;

    // Capture any unexpected errors
    Sentry.captureException(error, {
      tags: {
        module: "headless-task",
        eventName: event?.name || "unknown",
      },
      extra: {
        duration: taskDuration,
      },
    });

    geolocBgLogger.error("HeadlessTask error", {
      error,
      event,
      duration: taskDuration,
    });
  } finally {
    // Clear the timeout
    clearTimeout(taskTimeout);

    const finalDuration = Date.now() - taskStartTime;
    geolocBgLogger.debug("HeadlessTask completed", {
      event: event?.name,
      duration: finalDuration,
    });
  }
};

BackgroundGeolocation.registerHeadlessTask(HeadlessTask);
