async function fetchData() {
  await getDeviceID();
  const deviceData = await getAllData();

  let deviceID = localStorage.getItem("machineId");
  let deviceIP = deviceData.deviceLocationDetails.ipAddress;
  let timeZone = deviceData.deviceLocationDetails.timeZone;
  let screenOrientation = deviceData.deviceSize.orientation;
  let screenResolution =
    deviceData.deviceSize.orientation == "landscape"
      ? deviceData.deviceSize.screenWidth.toString() +
        "x" +
        deviceData.deviceSize.screenHeight.toString()
      : deviceData.deviceSize.screenHeight.toString() +
        "x" +
        deviceData.deviceSize.screenWidth.toString();
  let screenDetails = "Samsung";
  let googleLocation =
    deviceData.deviceLocationDetails.city +
    ", " +
    deviceData.deviceLocationDetails.state +
    ", " +
    deviceData.deviceLocationDetails.country;
  let postalCode = deviceData.deviceLocationDetails.postal;
  let latitude = deviceData.deviceLocationDetails.latitude;
  let longitude = deviceData.deviceLocationDetails.longitude;
  let platform = "Samsung";
  let height = deviceData.deviceSize.screenHeight;
  let width = deviceData.deviceSize.screenWidth;

  let body = {
    tempId: 0,
    otp: "",
    googleLocation: googleLocation,
    timeZone: timeZone,
    screenOrientation: screenOrientation,
    screenResolution: screenResolution,
    screenID: 0,
    height: height,
    width: width,
    macid: deviceID,
    ipAddress: deviceIP,
    postalCode: postalCode,
    latitude: latitude,
    longitude: longitude,
    userID: 0,
    os: platform,
    screendetails: screenDetails,
  };

  console.log("BODY: ", body);

  let baseUrl = "https://back.disploy.com/api/NewScreen/GeneratePreview";

  for (let attempt = 1; attempt <= 3; attempt++) {
    await waitForGeneratePreviewRateLimitSlot();

    try {
      const response = await axios.post(baseUrl, body);
      return response.data;
    } catch (error) {
      const isRateLimited = error?.response?.status === 429;
      if (isRateLimited && attempt < 3) {
        console.warn(
          `GeneratePreview 429 (attempt ${attempt}/3). Retrying in 3 seconds...`,
        );
        await sleep(GENERATE_PREVIEW_MIN_INTERVAL_MS);
        continue;
      }

      console.error("Error fetching data:", error);
      throw error;
    }
  }

  throw new Error("Failed to fetch GeneratePreview after retries");
}

const PREVIEW_HISTORY_THROTTLE_KEY = "generatePreview:history:last";
const PREVIEW_HISTORY_MIN_INTERVAL_MS = 120000;
const GENERATE_PREVIEW_LAST_REQUEST_KEY = "generatePreview:lastRequestAt";
const GENERATE_PREVIEW_MIN_INTERVAL_MS = 3000;

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function waitForGeneratePreviewRateLimitSlot() {
  try {
    const lastRequestAt = Number(
      localStorage.getItem(GENERATE_PREVIEW_LAST_REQUEST_KEY) || "0",
    );
    const now = Date.now();
    const waitMs = Math.max(
      0,
      lastRequestAt + GENERATE_PREVIEW_MIN_INTERVAL_MS - now,
    );

    if (waitMs > 0) {
      await sleep(waitMs);
    }

    localStorage.setItem(GENERATE_PREVIEW_LAST_REQUEST_KEY, String(Date.now()));
  } catch {
    await sleep(GENERATE_PREVIEW_MIN_INTERVAL_MS);
  }
}

function shouldThrottlePreviewHistoryCall(fingerprint) {
  try {
    const raw = localStorage.getItem(PREVIEW_HISTORY_THROTTLE_KEY);
    if (!raw) return false;

    const parsed = JSON.parse(raw);
    if (!parsed || typeof parsed.ts !== "number") return false;

    const isRecent = Date.now() - parsed.ts < PREVIEW_HISTORY_MIN_INTERVAL_MS;
    const isSame = parsed.fingerprint === fingerprint;
    return isRecent && isSame;
  } catch {
    return false;
  }
}

function markPreviewHistoryCall(fingerprint) {
  try {
    localStorage.setItem(
      PREVIEW_HISTORY_THROTTLE_KEY,
      JSON.stringify({ ts: Date.now(), fingerprint }),
    );
  } catch (error) {
    console.warn("Failed to persist preview history throttle metadata:", error);
  }
}

const DEFAULT_DEVICE_LOCATION_DETAILS = {
  ipAddress: "0.0.0.0",
  city: "Unknown",
  state: "Unknown",
  country: "Unknown",
  postal: "",
  latitude: "",
  longitude: "",
  timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC",
};

const IP_LOOKUP_MAX_WAIT_MS = 1200;

async function withTimeoutFallback(promise, fallbackValue, timeoutMs) {
  let timeoutId;
  const timeoutPromise = new Promise((resolve) => {
    timeoutId = setTimeout(() => resolve(fallbackValue), timeoutMs);
  });

  try {
    return await Promise.race([promise, timeoutPromise]);
  } finally {
    clearTimeout(timeoutId);
  }
}

async function getAllData() {
  const deviceLocationDetails = await withTimeoutFallback(
    lookupUserCountry().catch((error) => {
      console.warn("IP lookup failed, using default location details:", error);
      return DEFAULT_DEVICE_LOCATION_DETAILS;
    }),
    DEFAULT_DEVICE_LOCATION_DETAILS,
    IP_LOOKUP_MAX_WAIT_MS,
  );

  const deviceSize = await getScreenSize();
  return {
    deviceLocationDetails,
    deviceSize,
  };
}

async function getDeviceID() {
  const existingId = localStorage.getItem("machineId");
  if (existingId) {
    localStorage.setItem("deviceUID", existingId);
    return existingId;
  }

  return new Promise((resolve, reject) => {
    try {
      if (typeof webapis !== "undefined" && webapis.appcommon) {
        const value = webapis.appcommon.getUuid();
        localStorage.setItem("machineId", value);
        localStorage.setItem("deviceUID", value);
        resolve(value);
      } else {
        reject(new Error("webapis is not defined or unsupported"));
      }
    } catch (error) {
      reject(error);
    }
  });
}

async function getScreenSize() {
  var screenWidth = window.screen.width;
  var screenHeight = window.screen.height;
  var orientation = screenWidth > screenHeight ? "landscape" : "portrait";
  return { screenWidth, screenHeight, orientation };
}
async function timeoutFetch(url, options = {}, timeoutMs = 4000) {
  const controller = new AbortController();
  const id = setTimeout(() => controller.abort(), timeoutMs);
  try {
    const res = await fetch(url, { ...options, signal: controller.signal });
    clearTimeout(id);
    return res;
  } catch (err) {
    clearTimeout(id);
    throw err;
  }
}

async function lookupUserCountry() {
  const defaultResult = DEFAULT_DEVICE_LOCATION_DETAILS;

  const apiUrl =
    "https://api.ipregistry.co/?key=ira_cAwrJQJ06EjwpdaN0fr3cA5QiFA8cZ18JDx9";

  try {
    const response = await timeoutFetch(apiUrl, {}, 5000);

    if (!response.ok) {
      console.warn(
        "IP registry responded with non-ok status:",
        response.status,
        response.statusText,
      );
      return defaultResult;
    }

    const data = await response.json();

    const ipAddress = data.ip || defaultResult.ipAddress;
    const location = data.location || {};
    const city = (location.city && String(location.city)) || defaultResult.city;
    const state =
      (location.region &&
        location.region.name &&
        String(location.region.name)) ||
      defaultResult.state;
    const country =
      (location.country &&
        location.country.name &&
        String(location.country.name)) ||
      defaultResult.country;
    const postal = location.postal || defaultResult.postal;
    const latitude =
      (location.latitude !== undefined && String(location.latitude)) ||
      defaultResult.latitude;
    const longitude =
      (location.longitude !== undefined && String(location.longitude)) ||
      defaultResult.longitude;

    let timeZone =
      (data.time_zone && data.time_zone.abbreviation) ||
      (data.time_zone && data.time_zone.name) ||
      (data.timeZone && data.timeZone.abbreviation) ||
      Intl.DateTimeFormat().resolvedOptions().timeZone ||
      defaultResult.timeZone;

    return {
      ipAddress,
      city,
      state,
      country,
      postal,
      latitude,
      longitude,
      timeZone,
    };
  } catch (error) {
    console.error(
      "IP lookup failed (ipregistry). Bypassing and using fallbacks. Error:",
      error,
    );

    try {
      const pos = await new Promise((resolve, reject) => {
        if (!navigator.geolocation)
          return reject(new Error("geolocation unavailable"));
        const geoTimeout = setTimeout(
          () => reject(new Error("geolocation timeout")),
          5000,
        );
        navigator.geolocation.getCurrentPosition(
          (p) => {
            clearTimeout(geoTimeout);
            resolve(p);
          },
          (err) => {
            clearTimeout(geoTimeout);
            reject(err);
          },
          { maximumAge: 60_000, timeout: 5000 },
        );
      });

      const { coords } = pos;
      return {
        ipAddress: "0.0.0.0",
        city: "Unknown",
        state: "Unknown",
        country: "Unknown",
        postal: "",
        latitude: String(coords.latitude),
        longitude: String(coords.longitude),
        timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC",
      };
    } catch (geoErr) {
      console.warn("Geolocation fallback failed or denied:", geoErr);
      return defaultResult;
    }
  }
}

async function saveEmergencyMedia() {
  const loadingTextElement = document.querySelector(".loading-text");
  loadingTextElement.textContent = "Downloading Emergency Media...";
  const response = await fetch(data["myEmergencyMedia"]["assetFolderPath"]);
  if (!response.ok) {
    throw new Error("Failed to download media");
  }
  const blob = await response.blob();
  const objectURL = URL.createObjectURL(blob);
  localStorage.setItem("emergencyMedia", objectURL);
  loadingTextElement.textContent = "Loading...";
}

function checkVersionCompatibility(currentVersion, requiredVersion) {
  const current = (currentVersion || "0.0.0").split(".").map(Number);
  const required = (requiredVersion || "0.0.0").split(".").map(Number);

  const length = Math.max(current.length, required.length);

  for (let i = 0; i < length; i++) {
    const currentSegment = current[i] || 0;
    const requiredSegment = required[i] || 0;

    if (currentSegment < requiredSegment) return false;
    if (currentSegment > requiredSegment) return true;
  }

  return true;
}

async function handleNavigation() {
  try {
    console.log("Fetching new data...");
    const data = await fetchData();

    if (!data) {
      console.error("Error: Data not received");
      return;
    }

    handleNavigationFlow(data);
  } catch (error) {
    console.error("Error handling navigation:", error);

    if (error?.response?.status === 429) {
      setTimeout(() => {
        handleNavigation();
      }, GENERATE_PREVIEW_MIN_INTERVAL_MS);
      return;
    }

    if (typeof loaderManager !== "undefined") {
      loaderManager.hide();
    }
  }
}

/**
 * Helper function to extract media parameters from URL
 */
function extractMediaParams(url) {
  try {
    const urlObj = new URL(url);
    const mediaTypeID =
      parseInt(urlObj.searchParams.get("MediaTypeID") || "0", 10) || 0;
    const mediaID =
      parseInt(urlObj.searchParams.get("MediaID") || "0", 10) || 0;
    return { MediaTypeID: mediaTypeID, MediaID: mediaID };
  } catch (error) {
    console.error("Error parsing URL for media params:", error);
    return { MediaTypeID: 0, MediaID: 0 };
  }
}

/**
 * Helper function to extract mediaTypeID based on preview type
 */
function getMediaTypeID(data, previewType) {
  const finalPreviewType = previewType.toLowerCase();
  if (
    finalPreviewType === "composition" ||
    finalPreviewType === "asset" ||
    finalPreviewType === "screen group" ||
    finalPreviewType === "merge screen"
  ) {
    // For composition, extract from compositionPossition[0].schedules[0]
    try {
      const composition = data.myComposition;
      if (composition && composition.length > 0) {
        const positions = composition[0].compositionPossition;
        if (positions && positions.length > 0) {
          const schedules = positions[0].schedules;
          if (schedules && schedules.length > 0) {
            return schedules[0].mediaTypeID ?? 0;
          }
        }
      }
    } catch (error) {
      console.error("Error extracting mediaTypeID from composition:", error);
    }
    return 0;
  } else if (finalPreviewType === "app") {
    const url = data.apps?.[0]?.url || "";
    const params = extractMediaParams(url);
    return params.MediaTypeID ?? 0;
  } else if (finalPreviewType === "schedule") {
    // For schedule, extract from data[0] (first scheduled media)
    if (data.data && data.data.length > 0) {
      return data.data[0].mediaTypeID ?? 0;
    }
    return 0;
  } else {
    // For other types (default, etc), extract from data[0]
    if (data.data && data.data.length > 0) {
      return data.data[0].mediaTypeID ?? 0;
    }
    return 0;
  }
}

/**
 * Helper function to extract mediaID based on preview type
 */
function getMediaID(data, previewType) {
  const finalPreviewType = previewType.toLowerCase();
  if (
    finalPreviewType === "composition" ||
    finalPreviewType === "asset" ||
    finalPreviewType === "screen group" ||
    finalPreviewType === "merge screen"
  ) {
    // For composition, extract from compositionPossition[0].schedules[0]
    try {
      const composition = data.myComposition;
      if (composition && composition.length > 0) {
        const positions = composition[0].compositionPossition;
        if (positions && positions.length > 0) {
          const schedules = positions[0].schedules;
          if (schedules && schedules.length > 0) {
            return schedules[0].mediaID ?? 0;
          }
        }
      }
    } catch (error) {
      console.error("Error extracting mediaID from composition:", error);
    }
    return 0;
  } else if (finalPreviewType === "app") {
    const url = data.apps?.[0]?.url || "";
    const params = extractMediaParams(url);
    return params.MediaID ?? 0;
  } else if (finalPreviewType === "schedule") {
    // For schedule, extract from data[0] (first scheduled media)
    if (data.data && data.data.length > 0) {
      return data.data[0].mediaID ?? 0;
    }
    return 0;
  } else {
    // For other types (default, etc), extract from data[0]
    if (data.data && data.data.length > 0) {
      return data.data[0].mediaID ?? 0;
    }
    return 0;
  }
}

/**
 * Determine preview type based on navigation destination
 */
function determinePreviewType(data) {
  // 1. Check if data is empty (landing screen)
  if (!data.data || data.data.length === 0) {
    return "default";
  }

  // 2. Check multiple media items (display screen with schedules)
  if (data.data.length > 1) {
    return "schedule";
  }

  // 3. Check weather schedule
  if (data.myWeatherSchedule && data.myWeatherSchedule.length > 0) {
    return "schedule";
  }

  // 4. Check composition
  if (
    data.myComposition &&
    data.myComposition[0] &&
    data.myComposition[0].compositionPossition &&
    data.myComposition[0].compositionPossition.length > 0
  ) {
    return "composition";
  }

  // 5. Check apps/iframes
  if (data.apps && data.apps.length > 0) {
    return "app";
  }

  // 6. Default
  return "default";
}

/**
 * Call GeneratePreview API before navigation
 */
async function callGeneratePreviewAPI(data, previewType) {
  const currentTime = new Date();

  // Only call if data is available
  if (!data.data || data.data.length === 0) {
    console.log("Skipping GeneratePreview API call - no data available");
    return;
  }

  // Extract IDs based on preview type
  const mediaTypeID = getMediaTypeID(data, previewType);
  const mediaID = getMediaID(data, previewType);
  const screenID =
    data.data && data.data.length > 0 ? (data.data[0].screenID ?? 0) : 0;
  const macid = localStorage.getItem("machineId") || "";
  const fingerprint = `${macid}:${previewType}:${mediaTypeID}:${mediaID}:${screenID}`;

  if (shouldThrottlePreviewHistoryCall(fingerprint)) {
    console.log("Skipping GeneratePreview history call due to throttle window");
    return;
  }

  console.log(
    `📊 MediaTypeID: ${mediaTypeID}, MediaID: ${mediaID}, ScreenID: ${screenID}, MacID: ${macid}`,
  );

  try {
    // Create a promise that will timeout after 2 seconds
    const apiCall = generatePreviewService.addOrUpdatePreviewHistory({
      mediaTypeID: mediaTypeID,
      mediaID: mediaID,
      isDefaultAsset: previewType === "default",
      currentSystemDateTime: currentTime,
      screenID: screenID,
      previewType: previewType,
      systemStartTime: currentTime,
      macid: macid,
    });

    const timeout = new Promise((_, reject) =>
      setTimeout(() => reject(new Error("timeout")), 2000),
    );

    await Promise.race([apiCall, timeout]);
    markPreviewHistoryCall(fingerprint);
  } catch (error) {
    if (error.message === "timeout") {
      console.log("GeneratePreview API call timed out after 2 seconds");
    } else {
      console.log(
        `Error calling GeneratePreview API before navigation: ${error}`,
      );
    }
  }
}

async function handleNavigationFlow(data) {
  let buildVersion = localStorage.getItem("appVersion");
  let isCompatibileVersion = checkVersionCompatibility(
    buildVersion,
    data["myApplicationVersions"][0]["version"],
  );

  if (typeof loaderManager !== "undefined") {
    loaderManager.hide();
  }

  console.log("\n=== 🚀 Navigation Decision Process ===");
  console.log("⏰ Current time:", new Date().toISOString());

  // Determine the preview type based on screen decision
  const previewType = determinePreviewType(data);
  console.log("📊 Preview Type:", previewType);

  // Fire-and-forget to avoid blocking navigation and reduce perceived latency.
  callGeneratePreviewAPI(data, previewType);

  console.log("🎬 Final navigation decision");
  console.log("===============================\n");

  // Navigate based on data
  if (!isCompatibileVersion) {
    window.location.href = "update-screen.html";
  } else if (data.data.length === 0) {
    localStorage.removeItem("otp");
    localStorage.setItem("otp", data.otp);
    window.location.href = "landing-screen.html";
  } else if (data.data.length > 1) {
    window.location.href = "display-screen.html";
  } else if (
    data["myWeatherSchedule"] &&
    data["myWeatherSchedule"].length > 0
  ) {
    // Weather schedule takes priority (like android_new)
    console.log(
      "🌤️ Weather schedule detected, navigating to weather-schedule.html",
    );
    window.location.href = "weather-schedule.html";
  } else if (
    data["myComposition"] &&
    data["myComposition"][0]["compositionPossition"].length > 0
  ) {
    window.location.href = "composition-screen.html";
  } else if (data["apps"].length > 0) {
    window.location.href = "iframe-screen.html";
  } else {
    window.location.href = "display-screen.html";
  }
}

function init() {
  if (!localStorage.getItem("appVersion")) {
    localStorage.setItem("appVersion", "2.1.0");
  }
  handleNavigation();
}

document.addEventListener("DOMContentLoaded", init);
