qol.zed

qol updates for zed.city

// ==UserScript==
// @name         qol.zed
// @namespace    qol.zed.zero.nao
// @version      1.7
// @description  qol updates for zed.city
// @author       root
// @match        https://www.zed.city/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=zed.city
// @require      http://code.jquery.com/jquery-3.4.1.min.js
// @grant        none

// ==/UserScript==

let data = {};
let forumData = {};
let vehicleInventory = {};
let optimalInventory;
let weaponDurability = 100;

if (!localStorage.getItem("root-saved-timers")) {
  localStorage.setItem("root-saved-timers", JSON.stringify({}));
}
if (!localStorage.getItem("root-xp-display")) {
  localStorage.setItem("root-xp-display", "true");
}
if (!localStorage.getItem("root-market-data")) {
  localStorage.setItem("root-market-data", JSON.stringify({}));
}
if (!localStorage.getItem("root-exploration-data")) {
  localStorage.setItem("root-exploration-data", JSON.stringify({}));
}

let market_data = JSON.parse(localStorage.getItem("root-market-data"));

let store_items = {};
let current_inventory = {};
let limit;
let current_location = "City";

const ENERGY = "⚡";
const RAD = "☢";
const XP = "🏆";
const RAID = "🛡️";
const FURNACE = "🔥";
const RTOWER = "🗼";
const JUNKSTORE = "🛒";
const BOOSTER = "🍫";

const SEARCH = "🔍";
const TOGGLE = "🔃";

const ITEM_LIMIT = 360;
const TIMER_THRESHOLD = 10 * 60; // 10 minutes

const symbols = {
  energy: ENERGY,
  rad: RAD,
  xp: XP,
  raid: RAID,
  furnace: FURNACE,
  radio_tower: RTOWER,
  junk: JUNKSTORE,
  booster: BOOSTER,
};

const LOCATION_EXPLORATION_ID = {
  "Fuel Depot": 0,
  "Reclaim Zone": 1,
  "The Reserve": 2,
  "Military Base": 3,
  "Demolition Site": 4,
  "Construction Yard": 5,
};

(function () {
  const originalOpen = XMLHttpRequest.prototype.open;
  const originalSend = XMLHttpRequest.prototype.send;

  XMLHttpRequest.prototype.open = function (method, url) {
    this._interceptedURL = url;
    return originalOpen.apply(this, arguments);
  };

  XMLHttpRequest.prototype.send = function (body) {
    this.addEventListener("readystatechange", function () {
      if (this.readyState === 4) {
        try {
          const response = JSON.parse(this.responseText);
          const eventData = { url: this._interceptedURL, response };

          // Dispatch a custom event for other scripts
          window.dispatchEvent(
            new CustomEvent("xhrIntercepted", { detail: eventData }),
          );
        } catch (e) {
          console.error("Error processing intercepted response:", e);
        }
      }
    });

    return originalSend.apply(this, arguments);
  };
})();

window.addEventListener("xhrIntercepted", function (e) {
  const { url, response } = e.detail;
  if (url.includes("getStats")) {
    data = response;
    updateDisplay();
  } else if (url.includes("store_id")) {
    limit = response.limits?.limit || 0;
    let limitLeft = response.limits?.limit_left || 0;
    limit = limit - limitLeft;
    insertItemLimit(limit);
    if (url.includes("junk")) {
      handleJunkStore(response);
    }
    handleNPCStore(response);
  } else if (url.includes("getForum")) {
    handleForumData(response);
  } else if (url.includes("getStronghold")) {
    handleStronghold(response);
  } else if (url.includes("getRadioTower")) {
    handleRadioTower(response);
  } else if (url.includes("loadItems")) {
    handleWeaponDurability(response);
    calculateNetworth(response);
    handleVehicleInventory(response);
  } else if (url.includes("getMarket") && !url.includes("getMarketUser")) {
    saveMarketPrices(response);
  } else if (url.includes("getRoom")) {
    handleGetRoom(response);
    saveExplorationData(response);
  } else if (url.includes("getLocation")) {
    handleLocation(response);
  } else if (url.includes("startJob")) {
    handleStartJob(response);
  } else if (url.includes("getRecipes")) {
    handlegetRecipes(response);
  } else if (url.includes("getRadioTower")) {
    handleRadioTower(response);
  }
});

function createTimerDiv(name, link, timeLeft) {
  const div = document.createElement("a");
  div.href = link;
  div.innerText = symbols[name] || name;
  div.className = `${name}-root`;
  div.title = name.toUpperCase();
  const timeLeftDiv = document.createElement("div");
  timeLeftDiv.id = `${name}TimeLeft`;
  timeLeftDiv.className = "timer";
  timeLeftDiv.timeLeft = timeLeft;
  timeLeftDiv.innerText = getDayHourMinute(timeLeft);
  timeLeftDiv.style.cursor = "pointer";
  div.appendChild(timeLeftDiv);

  return div;
}

function updateDisplay() {
  const energyBar = document.querySelector("div.main-stat:nth-child(1)");
  const radBar = document.querySelector("div.rad");
  const moraleBar = document.querySelector("div.morale");
  const lifeBar = document.querySelector("div.life");
  const money = document.querySelector(".stat-money");
  const xpIcon = document.querySelector(
    ".q-col-gutter-md > div:nth-child(3) > div:nth-child(1)",
  );
  if (!(energyBar && radBar && moraleBar && lifeBar && xpIcon && money)) {
    setTimeout(updateDisplay, 200);
    return;
  }
  let dataContainer = document.getElementById("dataContainer");
  if (!dataContainer) {
    dataContainer = document.createElement("div");
    dataContainer.id = "dataContainer";
    dataContainer.style = `
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 15px;
    margin: 0 auto;
    background-color: rgba(9, 10, 11);
    font-family: Oswald,sans-serif;
    font-size: 15px;
    padding-left: 5px;
    padding-right: 5px;
`;
    document
      .querySelector(".q-toolbar > div:nth-child(1)")
      .parentElement.insertAdjacentElement("afterend", dataContainer);
  }
  let energyMax = data.membership ? 150 : 100;
  let energyRegenRate = data.membership ? 0.5 : 1 / 3;
  let radMax = data.stats.max_rad;
  let radRegenRate = 1 / 5;

  let energyLeft = energyMax - data.energy - 5;
  let radLeft = radMax - data.rad - 1;

  let energyTimeLeft =
    (energyLeft / energyRegenRate) * 60 + data.energy_regen || 0;
  let radTimeLeft = (radLeft / radRegenRate) * 60 + data.rad_regen || 0;

  let energyLeftDiv = document.getElementById("energyTimeLeft");
  if (energyLeftDiv) {
    energyLeftDiv.textContent = getDayHourMinute(energyTimeLeft);
    energyLeftDiv.timeLeft = energyTimeLeft;
  } else {
    const energyDiv = createTimerDiv(
      "energy",
      "/stronghold/1781362",
      energyTimeLeft,
    );
    dataContainer.appendChild(energyDiv);
  }

  let radLeftDiv = document.getElementById("radTimeLeft");
  if (radLeftDiv) {
    radLeftDiv.textContent = getDayHourMinute(radTimeLeft);
    radLeftDiv.timeLeft = radTimeLeft;
  } else {
    const radDiv = createTimerDiv("rad", "/scavenge", radTimeLeft);
    dataContainer.appendChild(radDiv);
  }

  let xpEnd = Math.round(data.xp_end);
  let xpNow = Math.round(data.experience);

  if ($("#xpData").length > 0) {
    $("#xpData").text(`${symbols.xp} ${xpEnd - xpNow}`);
  } else {
    const xpData = document.createElement("div");
    xpData.id = "xpData";
    xpData.className = "xp-root";
    xpData.innerHTML = `${symbols.xp} ${xpEnd - xpNow} <span id='xpCurrentHigh'>[${xpNow}/${xpEnd}]</span>`;
    xpData.title = "XP";
    dataContainer.appendChild(xpData);

    const currentDisplay = localStorage.getItem("root-xp-display") == "true";
    if (currentDisplay) {
      $("#xpCurrentHigh").css("display", "inline");
    } else {
      $("#xpCurrentHigh").css("display", "none");
    }

    xpData.addEventListener("click", () => {
      $("#xpCurrentHigh").css(
        "display",
        $("#xpCurrentHigh").css("display") == "none" ? "inline" : "none",
      );
      localStorage.setItem(
        "root-xp-display",
        $("#xpCurrentHigh").css("display") == "none" ? "false" : "true",
      );
    });
  }

  data.raid_cooldown = data.raid_cooldown || -1;
  let raidTimeLeft = getDayHourMinute(data.raid_cooldown);
  let raidTimeLeftDiv = document.getElementById("raidTimeLeft");
  if (raidTimeLeftDiv) {
    raidTimeLeftDiv.textContent = raidTimeLeft;
    raidTimeLeftDiv.timeLeft = data.raid_cooldown;
  } else {
    const raidDiv = createTimerDiv("raid", "/raids", data.raid_cooldown);
    dataContainer.appendChild(raidDiv);
  }

  // add booster
  const booserCd = data.booster_cooldown || -1;

  let boosterTimeLeft = document.getElementById("boosterTimeLeft");
  if (boosterTimeLeft) {
    boosterTimeLeft.textContent = getDayHourMinute(booserCd);
    boosterTimeLeft.timeLeft = booserCd;
  } else {
    const boosterDiv = createTimerDiv("booster", "/inventory", booserCd);
    dataContainer.appendChild(boosterDiv);
  }

  addAdditionalTimers(dataContainer);
  addOtherTimersDropDown(dataContainer);
}

function addAdditionalTimers(dataContainer) {
  const savedTimers = JSON.parse(localStorage.getItem("root-saved-timers"));
  const currentTime = new Date();
  for (const timer in savedTimers) {
    const finishTimeinSeconds = savedTimers[timer].finishTimeinSeconds * 1;
    const timeLeft = Math.round(
      (finishTimeinSeconds - currentTime.getTime()) / 1000,
    );
    const link = savedTimers[timer].url;
    let timerLeftDiv = document.getElementById(timer + "TimeLeft");
    if (timerLeftDiv) {
      timerLeftDiv.timeLeft = timeLeft;
    } else {
      const additionalTimerDiv = createTimerDiv(timer, link, timeLeft);
      dataContainer.appendChild(additionalTimerDiv);
    }
  }
}

function getDayHourMinute(seconds) {
  if (seconds <= 0) {
    return "00:00";
  }

  let time = [];
  let days = Math.floor(seconds / 86400);
  seconds -= days * 86400;
  let hours = Math.floor(seconds / 3600);
  seconds -= hours * 3600;
  let minutes = Math.floor(seconds / 60);
  seconds -= minutes * 60;

  days = days ? (days < 10 ? "0" + days : days) : "00";
  hours = hours ? (hours < 10 ? "0" + hours : hours) : "00";
  minutes = minutes ? (minutes < 10 ? "0" + minutes : minutes) : "00";
  seconds = seconds ? (seconds < 10 ? "0" + seconds : seconds) : "00";

  if (days != "00" || time.length > 0) {
    time.push(days);
  }
  if (hours != "00" || time.length > 0) {
    time.push(hours);
  }
  if (minutes != "00" || time.length > 0) {
    time.push(minutes);
  }
  if (seconds != "00" || time.length > 0) {
    time.push(seconds);
  }

  return time.join(":");
}

function updateTimerValues() {
  const savedTimers = JSON.parse(localStorage.getItem("root-saved-timers"));
  const currentTime = new Date();
  for (const timer in savedTimers) {
    const element = document.querySelector("#" + timer + "TimeLeft");
    if (!element) continue;
    const finishTimeinSeconds = savedTimers[timer].finishTimeinSeconds * 1;
    const timeLeft = Math.round(
      (finishTimeinSeconds - currentTime.getTime()) / 1000,
    );
    element.timeLeft = timeLeft;
  }
}

function updateTimers() {
  //console.log("updateTimers");
  document.querySelectorAll(".timer").forEach((timer) => {
    if (timer.timeLeft * 1 <= -1) {
      //console.log("Found timer with timeLeft <= -1");
      timer.classList.add("alert");
      return;
    }
    //console.log("Updating timer with timeLeft", timer.timeLeft);
    const timeLeft = timer.timeLeft - 1;
    timer.timeLeft = timeLeft;

    if (timeLeft <= TIMER_THRESHOLD) {
      timer.classList.add("alert");
    } else {
      timer.classList.remove("alert");
    }
    if (timeLeft == 0) {
      timer.timeLeft = -1;
      const timerType = timer.id.replace("TimeLeft", "");
      let message = "";
      let link = "";
      const timerName = timerType.replaceAll("_", " ");

      if (timerType == "energy") {
        message = "Energy is full!";
        link = "/stronghold/1781362";
      } else if (timerType == "rad") {
        message = "Rad is full!";
        link = "/scavenge";
      } else if (timerType == "raid") {
        message = "Raid timer is up!";
        link = "/raids";
      } else if (timerType == "furnace") {
        message = "Furnace is empty!";
        link = "/stronghold/1781365";
      } else if (timerType == "radio_tower") {
        message = "Radio Tower has refreshed!";
        link = "/stronghold/1781367";
      } else if (timerType == "junk") {
        message = "Junk store buy is ready!";
        link = "/store/junk";
      } else if (timerType == "booster") {
        message = "Booster Cooldown is empty!";
        link = "/inventory";
      } else {
        message = `${timerName} is up!`;
      }
      sendNotification("Zed City [root]", message, link);
    }
    timer.innerText = getDayHourMinute(timeLeft);
  });
}

function sendNotification(title, body, link) {
  if (!("Notification" in window)) {
    // console.log("This browser does not support desktop notification");
  } else if (Notification.permission !== "denied") {
    Notification.requestPermission().then((permission) => {
      if (permission === "granted") {
        const notification = new Notification(title, {
          body: body,
          icon: "https://www.google.com/s2/favicons?sz=64&domain=zed.city",
        });
        notification.onclick = () => {
          window.open(link);
          notification.close();
        };
      }
    });
  }
}

function insertItemLimit(n) {
  const element = document.querySelector(".text-h4");
  if (element) {
    if ($("#itemLimit").length > 0) {
      $("#itemLimit").text(` [ ${n} / ${ITEM_LIMIT} ]`);
    } else {
      element.innerHTML += `<div id="itemLimit"> [ ${n} / ${ITEM_LIMIT} ]</div>`;
    }

    element.style.color = n < ITEM_LIMIT ? "#00ff00" : "#ff0000";
  } else {
    setTimeout(insertItemLimit, 100, n);
  }
}

function updateMembership() {
  const tooltip = document.querySelector(".q-tooltip");
  if (!tooltip || !tooltip.textContent.includes("Membership")) {
    setTimeout(updateMembership, 300);
    return;
  }

  if (document.querySelector("#membershipTimer")) {
    document.querySelector("#membershipTimer").remove();
  }
  const membershipTimer = document.createElement("div");
  membershipTimer.id = "membershipTimer";
  membershipTimer.className = "timer full-width justify-center";
  membershipTimer.timeLeft = data.membership_expire;
  membershipTimer.innerText = getDayHourMinute(membershipTimer.timeLeft);
  tooltip.appendChild(membershipTimer);
}

setInterval(updateTimers, 1000);

const style = document.createElement("style");
style.innerHTML = `
  .timer {
    color: white;
    font-weight: bold;
    text-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
    text-decoration: none !important;
    display: inline-block;
    width: 60px;
    text-align: center;
  }

  .energy-root {
    color: #ffb74d;
  }
  .rad-root {
    color: #66bb6a;
  }
  .xp-root {
    color: #9A57CC;
  }
  .raid-root {
    color: #f04949;
  }

  a {
    text-decoration: none;
  }

  .alert {
    color: red;
  font-size: 20px;
  font-weight: bold;
  }


  .arrow-menu {
    position: relative;
  }

  .menu-options {
      position: absolute;
      z-index: 100;
      margin-top: 5px;
      border-radius: 4px;
      box-shadow: 0 2px 8px rgba(0,0,0,0.1);
      overflow: hidden;
      width: 200px;
      background: black;
      right: 0;
    }

    .menu-item {
      padding: 12px 15px;
      border-bottom: 1px solid #eee;
      cursor: pointer;
      transition: background-color 0.2s;
      overflow: auto;

      .timer {
        float: right;
      }
    }

    .menu-item:last-child {
      border-bottom: none;
    }

    .menu-item:hover {
      background-color: rgba(255, 255, 255, 0.1);
    }

table {
            border-collapse: collapse;
            width: 100%;
            margin-bottom: 20px;
        }
        th, td {
            border: 1px solid #ddd;
            padding: 8px;
            text-align: left;
        }
        th {
        }

  .exploration-data-container {
    background: #3d474a;
    font-family: ZedFont, sans-serif;
    font-size: 18px;
  }

  .warning {
    position: absolute;
    left:0;
    background-color: #ff4242;
    border-radius: 5px;
    z-index: 999999999;
    height: 48px;
    padding: 14px;
    margin: 10px;
    transition: bottom 1s ease-out, opacity 0.5s ease-in-out;
  }

  .profit-div {
    font-family: Oswald;
  }

  li {
    overflow: auto;
  }

  

`;
document.head.appendChild(style);

function waitForElement(selector) {
  return new Promise((resolve) => {
    if (document.querySelector(selector)) {
      return resolve(document.querySelector(selector));
    }

    const observer = new MutationObserver((mutations) => {
      if (document.querySelector(selector)) {
        observer.disconnect();
        resolve(document.querySelector(selector));
      }
    });

    observer.observe(document.documentElement, {
      childList: true,
      subtree: true,
    });
  });
}

function forumUpdate() {
  // console.log("forum update");
  if (!window.location.href.includes("/forum")) {
    return;
  }
  $(".username:not([processed])").each(function () {
    const username = $(this).text();
    // console.log(username);
    if (forumData[username]) {
      $(this).wrap(`<a href="/profile/${forumData[username].userId}"></a>`);
    }
    $(this).attr("processed", true);
  });
  $(".forum-username:not([processed])").each(function () {
    const username = $(this).text();
    if (forumData[username]) {
      $(this).wrap(`<a href="/profile/${forumData[username].userId}"></a>`);
    }
    $(this).attr("processed", true);
  });
  setTimeout(forumUpdate, 1000);
}

function handleForumData(response) {
  // console.log("handling forum data");
  if (response.topics) {
    for (const topic of response.topics) {
      const authorID = topic.author.id;
      const author = topic.author.username;
      if (!forumData[author]) {
        forumData[author] = {
          userId: authorID,
        };
      }
      if (topic.reply_user) {
        const replyUserID = topic.reply_user.id;
        const replyUser = topic.reply_user.username;
        if (!forumData[replyUser]) {
          forumData[replyUser] = {
            userId: replyUserID,
          };
        }
      }
    }
  }
  if (response.posts) {
    for (const post of response.posts) {
      const authorID = post.author.id;
      const author = post.author.username;
      if (!forumData[author]) {
        forumData[author] = {
          userId: authorID,
        };
      }
    }
  }

  if (response.topic) {
    const authorID = response.topic.author.id;
    const author = response.topic.author.username;
    if (!forumData[author]) {
      forumData[author] = {
        userId: authorID,
      };
    }
  }
}

function checkPage() {
  if (window.location.href.includes("/forum")) {
    forumUpdate();
  } else if (window.location.href.includes("/stronghold/1781362")) {
    maxGymVal();
  }
}

function handleStronghold(response) {
  for (const strongholdId in response.stronghold) {
    const stronghold = response.stronghold[strongholdId];
    const codename = stronghold.codename;
    if (codename == "furnace") {
      const inProgress = stronghold.in_progress;
      if (!inProgress) {
        const savedTimers = JSON.parse(
          localStorage.getItem("root-saved-timers"),
        );
        savedTimers["furnace"] = {
          finishTimeinSeconds: -1,
          url: window.location.href,
        };
        localStorage.setItem("root-saved-timers", JSON.stringify(savedTimers));

        return;
      }

      const totalItems = stronghold?.items["item_requirement-1"]?.quantity;
      const timeEach =
        stronghold?.items["item_requirement-bp"]?.vars?.wait_time;
      const quantityEach =
        stronghold?.items["item_requirement-bp"]?.vars?.items?.[
          "item_requirement-1"
        ]?.qty;
      const completedSoFar = stronghold.iterationsPassed || 0;
      const currentTimeLeft = stronghold.timeLeft;

      if (timeEach && quantityEach && currentTimeLeft && totalItems) {
        const iterationsLeft = totalItems / quantityEach - completedSoFar - 1;
        const waitTimeLeft = timeEach * iterationsLeft + currentTimeLeft;
        const currenTime = new Date();
        const finishTimeinSeconds = currenTime.getTime() + waitTimeLeft * 1000;

        const savedTimers = JSON.parse(
          localStorage.getItem("root-saved-timers"),
        );
        savedTimers["furnace"] = {
          finishTimeinSeconds: finishTimeinSeconds,
          url: window.location.href,
        };
        localStorage.setItem("root-saved-timers", JSON.stringify(savedTimers));
      }
    }
  }
  updateTimerValues();
}

function handleRadioTower(response) {
  const expire = response.expire;
  if (expire) {
    const currenTime = new Date();
    const finishTimeinSeconds = currenTime.getTime() + expire * 1000;

    const savedTimers = JSON.parse(localStorage.getItem("root-saved-timers"));
    savedTimers["radio_tower"] = {
      finishTimeinSeconds: finishTimeinSeconds,
      url: window.location.href,
    };
    localStorage.setItem("root-saved-timers", JSON.stringify(savedTimers));
  }
  updateTimerValues();

  handleRadioTowerValues(response);
}

function handleJunkStore(response) {
  const expire = response.limits?.reset_time;
  if (expire) {
    const currenTime = new Date();
    const finishTimeinSeconds = currenTime.getTime() + expire * 1000;

    const savedTimers = JSON.parse(localStorage.getItem("root-saved-timers"));
    savedTimers["junk"] = {
      finishTimeinSeconds: finishTimeinSeconds,
      url: window.location.href,
    };
    localStorage.setItem("root-saved-timers", JSON.stringify(savedTimers));
  }
  updateTimerValues();
}

function updateServerTime() {
  if ($(".server-time").length == 0) {
    setTimeout(updateServerTime, 1000);
    return;
  }
  const serverTime = document.querySelector(".server-time");
  serverTime.style.whiteSpace = "nowrap";
  if (serverTime) {
    const logo_body = document.querySelector(".logo-body");
    logo_body.appendChild(serverTime);
  }
}

updateServerTime();

// Gym value prefill
function maxGymVal() {
  if ($("input").length < 4 || !data.energy) {
    setTimeout(maxGymVal, 1000);
    return;
  }
  const energy = data.energy;
  const iterations = energy / 5;

  $("input:not('processed')").each(function () {
    this.value = iterations;
    this.dispatchEvent(new Event("input"), { bubbles: true });
    $(this).attr("processed", true);
  });
}

// calls appropriate functions based on url
async function handleURL() {
  setTimeout(async function () {
    let url = window.location.href;

    // console.log("handling url", url);
    if (url.includes("/forum")) {
      forumUpdate();
    } else if (url.includes("/stronghold/1781362")) {
      maxGymVal();
    } else if (url.includes("inventory")) {
      handleInventory();
      if (data.exploring) {
        showOptimalInventory();
      }
    } else if (url.includes("explore")) {
      display_exploration_data();
      checkVehicleInventoryWeight();
    } else if (url.includes("hunt")) {
      const latestLoadItems = await getLatestLoadItems();
      handleWeaponDurability(latestLoadItems);
      checkWeaponDurability();
    }
  }, 500);
}

let url = window.location.href;
window.addEventListener("hashchange", handleURL);
window.addEventListener("popstate", handleURL);

const originalPushState = history.pushState;
history.pushState = function (...args) {
  originalPushState.apply(this, args);
  handleURL();
};

const originalReplaceState = history.replaceState;
history.replaceState = function (...args) {
  originalReplaceState.apply(this, args);
  handleURL();
};

// Store element states
const elementStates = new Map();

// Configure the elements you want to watch
const elementsToWatch = [];

// Function to check a single element
function checkElement(config) {
  const url = config.url;
  if (url && !window.location.href.includes(url)) {
    return;
  }

  const element = document.querySelector(config.selector);
  const isFound = element?.textContent === config.text;
  const wasPresent = elementStates.get(config.selector);

  if (isFound && !wasPresent) {
    // Element has appeared

    // console.log(config.text + " appeared!");
    elementStates.set(config.selector, true);
    config.callback();
  } else if (!isFound && wasPresent) {
    // Element has disappeared
    // console.log(config.text + " disappeared!");
    elementStates.set(config.selector, false);
  }
}
//
// Create observer that checks all elements
const observer = new MutationObserver(() => {
  elementsToWatch.forEach(checkElement);
});

// Start observing
observer.observe(document.documentElement, {
  childList: true,
  subtree: true,
});

// Example of how to add a new element to watch later:
function addElementToWatch(selector, text, url, callback) {
  elementsToWatch.push({ selector, text, url, callback });
  elementStates.set(selector, false);
}

addElementToWatch(".text-bold", "Membership Expires", "", () => {
  if (!data.membership) {
    return;
  }
  // console.log("updating membership");
  updateMembership();
});

// market price
addElementToWatch(".title div", "Create Listing", "/market-listings", () => {
  insertPrice();
});

// store qty
addElementToWatch(
  ".text-negative > span:nth-child(2) > span:nth-child(1)",
  "Cancel",
  "store/",
  () => {
    // console.log("store qty appeared!");
    insertQty();
  },
);

// furnace max items
addElementToWatch(
  "div.col-xs-6:nth-child(2) > button:nth-child(1) > span:nth-child(2) > span:nth-child(1)",
  "Craft",
  "/stronghold/1781365",
  () => {
    maxFurnaceVal();
  },
);

function handleInventory() {
  // console.log("handling inventory");
  if ($(".q-px-xs").length == 0) {
    setTimeout(handleInventory, 100);
    return;
  }

  if (!localStorage.getItem("root-itemworth")) {
    return;
  }
  // console.log("handling inventory");
  let itemworth = localStorage.getItem("root-itemworth");
  itemworth = Math.round(itemworth).toLocaleString();
  itemworth = "$" + itemworth;

  if ($("#root-itemworth").length > 0) {
    $("#root-itemworth").html(
      `<div id="root-itemworth"><span>Networth: </span><span id="networth">${itemworth}</span></div>`,
    );
  } else {
    const itemworthData = `<div id="root-itemworth"><span>Networth: </span><span id="networth">${itemworth}</span></div>`;
    $(".zed-grid").prepend(itemworthData);
  }
}

function calculateNetworth(response) {
  let networth = 0;
  if (response.items) {
    for (const item of response.items) {
      const itemName = item.name;
      const itemQuantity = item.quantity;
      if (market_data[itemName]) {
        const itemPrice = market_data[itemName];
        const itemValue = itemPrice * itemQuantity;
        networth += itemValue;
      }
    }
  }
  localStorage.setItem("root-itemworth", networth);
}

function addItemWorth() {
  $(".q-item__label:not([processed])").each(function () {
    const itemName = $(this).text();
    $(this).attr("processed", true);
    if (market_data[itemName]) {
      const itemPrice = market_data[itemName];
      const itemValue = itemPrice * store_items[itemName];
      $(this).append(`<div id="itemWorth">[${itemValue}]</div>`);
    }
  });
}

function saveMarketPrices(response) {
  for (const item of response.items) {
    const name = item.name;
    const price = item.market_price;
    market_data[name] = price;
  }
  localStorage.setItem("root-market-data", JSON.stringify(market_data));
}

function handleNPCStore(response) {
  limit = response.limits?.limit || 0;
  let limitLeft = response.limits?.limit_left || 0;
  limit = limit - limitLeft;
  // console.log(limit);

  for (const item of response.storeItems) {
    const name = item.name;
    const quantity = item.quantity;
    store_items[name] = quantity;
  }
  for (const item of response.userItems) {
    const name = item.name;
    const quantity = item.quantity;
    current_inventory[name] = quantity;
  }
}

function insertPrice() {
  if ($(".q-py-sm > div:nth-child(1)").length == 0) {
    setTimeout(insertPrice, 100);
    return;
  }

  const itemName = document.querySelector(
    ".q-py-sm > div:nth-child(1)",
  ).textContent;
  if (market_data[itemName]) {
    const price = market_data[itemName];
    const priceElementParet = document.querySelector(".zed-money-input");

    if (!priceElementParet) {
      setTimeout(insertPrice, 300);
      return;
    }
    const priceElement = priceElementParet.querySelector("input");

    if (!priceElement) {
      setTimeout(insertPrice, 100);
      return;
    }
    priceElement.value = price;
    priceElement.dispatchEvent(new Event("input", { bubbles: true }));
    $(
      ".zed-money-input > div:nth-child(1) > div:nth-child(1) > div:nth-child(1) > i:nth-child(1)",
    ).css("color", "#da5aec");
  }
}

function insertQty() {
  const subtextElement = document.querySelector("div.grid-cont:nth-child(1)");
  const quantityElement = subtextElement.querySelector("input");
  const itemName = document.querySelector(
    ".small-modal > div:nth-child(1)",
  ).textContent;

  if (!quantityElement || !itemName) {
    console.log("quantity element: ", quantityElement);
    console.log("item name: ", itemName);
    setTimeout(insertQty, 500);
    return;
  }

  const quantity = Math.min(ITEM_LIMIT - limit, store_items[itemName] || 0);
  const isBuying = document
    .querySelector(
      "div.text-center:nth-child(2) > button:nth-child(1) > span:nth-child(2) > span:nth-child(1)",
    )
    .textContent.includes("Buy");
  if (isBuying) {
    quantityElement.value = quantity;
  } else {
    const inventoryQuantity = current_inventory[itemName] || 0;
    quantityElement.value = inventoryQuantity;
  }
  quantityElement.dispatchEvent(new Event("input", { bubbles: true }));
}

function maxFurnaceVal() {
  const container = document.querySelector(".small-modal");
  const inputElement = container.querySelector("input");
  if (!inputElement) {
    setTimeout(maxFurnaceVal, 100);
    return;
  }
  inputElement.value = 9999999;
  inputElement.dispatchEvent(new Event("input", { bubbles: true }));
}

function handleGetRoom(response) {
  const respawn_times = [];
  if (response.npcs) {
    for (const npc of response.npcs) {
      const respawn_time = npc.vars.respawn_time;
      if (respawn_time - 0 > 0) {
        respawn_times.push(respawn_time);
      }
    }
  }

  if (respawn_times.length > 0) {
    addRespawnTime(respawn_times);
  }
}

function addRespawnTime(respawn_times) {
  const elements = document.querySelectorAll(".defeated-text");
  if (elements.length == 0) {
    setTimeout(addRespawnTime, 100, respawn_times);
    return;
  }

  for (let i = 0; i < elements.length; i++) {
    const respawn_time = respawn_times[i];
    if (respawn_time) {
      if (elements[i].querySelector(".timer")) {
        elements[i].querySelector(".timer").remove();
      }

      const newTimer = document.createElement("div");
      newTimer.timeLeft = respawn_time;
      newTimer.className = "timer";
      newTimer.innerText = getDayHourMinute(respawn_time);
      newTimer.style.marginLeft = "10px";
      elements[i].appendChild(newTimer);
    }
  }
}

function scavengeButton() {
  const scavengeBtnStyle = document.createElement("style");
  scavengeBtnStyle.innerHTML = `
    button[data-cy="scavenge-btn"] {
          position: fixed;
          transform: translateX(-50%);
          left: 50%;
          top: 70%;
      }

      @media (max-width: 450px) {
          button[data-cy="scavenge-btn"] {
              top: 80%;
          }
      }
`;

  document.head.appendChild(scavengeBtnStyle);
}

function handleVehicleInventory(response) {
  if (current_location == "City") return;
  const vehicle_items = response.vehicle_items;
  if (!vehicle_items) return;
  market_data = JSON.parse(localStorage.getItem("root-market-data"));

  for (const item of vehicle_items) {
    const item_name = item.name;
    const item_quantity = item.quantity;
    const item_weight = item.vars.weight;

    if (market_data[item_name]) {
      vehicleInventory[item_name] = {
        amount: item_quantity,
        weight: item_weight,
        val: market_data[item_name],
      };
    }
  }

  optimalInventory = optimalItemsExploring(vehicleInventory, 20);
}

function optimalItemsExploring(itemsDict, capacity) {
  // Define a scaling factor to convert decimal weights to integers
  const SCALE_FACTOR = 100; // Allows for 2 decimal places

  // Scale capacity to an integer
  const scaledCapacity = Math.floor(capacity * SCALE_FACTOR);

  // Convert the problem to a standard 0/1 knapsack by "unpacking" items
  const items = [];
  const weights = [];
  const itemKeys = [];
  const originalIndices = []; // To track which original item each unpacked item belongs to

  let index = 0;
  for (const [key, details] of Object.entries(itemsDict)) {
    const { val, amount, weight } = details;

    // Handle fractional amounts (e.g., 0.5 units)
    const unitsToAdd = amount >= 1 ? Math.floor(amount) : 1;

    // For each unit of this item (up to amount), add as a separate item
    for (let i = 0; i < unitsToAdd; i++) {
      items.push(val);
      // Scale the weight to an integer
      weights.push(Math.floor(weight * SCALE_FACTOR));
      itemKeys.push(key);
      originalIndices.push(index);
    }
    index++;
  }

  const itemsCount = items.length;
  let optimalTable = Array.from({ length: itemsCount + 1 }, () =>
    Array(scaledCapacity + 1).fill(0),
  );

  // Fill the dynamic programming table
  for (let i = 1; i <= itemsCount; i++) {
    for (let j = 0; j <= scaledCapacity; j++) {
      if (weights[i - 1] <= j) {
        optimalTable[i][j] = Math.max(
          optimalTable[i - 1][j],
          optimalTable[i - 1][j - weights[i - 1]] + items[i - 1],
        );
      } else {
        optimalTable[i][j] = optimalTable[i - 1][j];
      }
    }
  }

  // Reconstruct the solution
  const selectedItemsWithDuplicates = [];
  let remainingCapacity = scaledCapacity;

  for (let i = itemsCount; i >= 1; i--) {
    if (
      optimalTable[i][remainingCapacity] >
      optimalTable[i - 1][remainingCapacity]
    ) {
      selectedItemsWithDuplicates.push({
        key: itemKeys[i - 1],
        weight: weights[i - 1] / SCALE_FACTOR, // Convert back to decimal for output
      });
      remainingCapacity -= weights[i - 1];
    }
  }

  // Count occurrences of each selected item
  const selectedItemsCounts = {};
  let totalWeight = 0;

  for (const item of selectedItemsWithDuplicates) {
    selectedItemsCounts[item.key] = (selectedItemsCounts[item.key] || 0) + 1;
    totalWeight += item.weight;
  }

  // Convert to array format with counts
  const selectedItems = Object.entries(selectedItemsCounts).map(
    ([item, count]) => ({
      item,
      count,
    }),
  );

  return {
    maxValue: optimalTable[itemsCount][scaledCapacity],
    selectedItems: selectedItems,
    selectedItemsRaw: selectedItemsWithDuplicates.reverse(), // For debugging
    totalWeight: totalWeight, // Show the actual used weight
  };
}

function showOptimalInventory() {
  optimalInventory = optimalInventory;
  if (!optimalInventory) {
    return;
  }
  const optimalItems = {};
  for (const item of optimalInventory.selectedItems) {
    optimalItems[item.item] = item.count;
  }
  document.querySelectorAll(".q-item__label").forEach((item) => {
    const itemName = item.innerText.split("\n")[0];
    const amt = optimalItems[itemName] || 0;
    const currentAmt =
      item.parentElement.parentElement
        .querySelector(".item-qty")
        ?.innerText.replace("x", "") || 1;
    const span = item.querySelector("span") ?? document.createElement("span");
    span.innerText = `Amt: [${amt}]`;
    span.style.float = "right";
    span.style.color = amt - currentAmt < 0 || amt == 0 ? "red" : "green";
    item.appendChild(span);
  });

  if (window.location.href.includes("inventory")) {
    setTimeout(showOptimalInventory, 1000);
  }
}

function handleLocation(response) {
  current_location = response.name;
}

function saveExplorationData(response) {
  const savedExplorationDataAll = JSON.parse(
    localStorage.getItem("root-exploration-data"),
  );
  const savedExplorationData = savedExplorationDataAll[current_location] || {};
  const npcs = savedExplorationData.npcs || {};
  const gates = savedExplorationData.gates || {};
  const scavenge = savedExplorationData.scavenge || {};
  const current_time = new Date();

  for (const npc of response.npcs) {
    const npc_name = npc.name;
    const np_code = npc.id;
    const life = npc.vars.life;
    const max_life = npc.vars.max_life;
    const respawn_time_left = npc.vars.respawn_time;
    const respawn_time = current_time.getTime() + respawn_time_left * 1000;
    const push_data = {
      name: npc_name,
      life: life,
      max_life: max_life,
      respawn_time: respawn_time,
    };
    npcs[np_code] = push_data;
  }

  for (const gate of response.gates) {
    const gate_code = gate.id;
    const gate_name = gate.name;
    const gate_unlocked = gate.vars.unlocked || false;
    const gate_unlock_time = gate.vars.unlock_time || 0;
    const gate_unlocked_till = current_time.getTime() + gate_unlock_time * 1000;
    const required_items = {};
    for (const item_key in gate.items) {
      const item = gate.items[item_key];
      const item_name = item.name;
      const item_quantity = item.req_qty;
      required_items[item_name] = item_quantity;
    }
    const gate_data = {
      name: gate_name,
      unlocked: gate_unlocked,
      gate_unlock_time: gate_unlocked_till,
      required_items: required_items,
    };
    gates[gate_code] = gate_data;
  }

  const scavenge_invalid = [
    "Fuel Pumps",
    "Foundation Pit",
    "Bulk Goods Lockup",
    "Scrap Pile",
  ];
  for (const scavengeD of response.scavenge) {
    const scavenge_code = scavengeD.id;
    const scavenge_name = scavengeD.name;

    if (scavenge_invalid.includes(scavenge_name)) {
      continue;
    }

    const scavenge_cooldown = scavengeD.vars.cooldown;
    const scavenge_cooldown_end = scavenge_cooldown
      ? current_time.getTime() + scavenge_cooldown * 1000
      : -1;
    const scavenge_push_data = {
      name: scavenge_name,
      cooldown: scavenge_cooldown,
      cooldown_end: scavenge_cooldown_end,
    };
    scavenge[scavenge_code] = scavenge_push_data;
  }

  const exploration_data = JSON.parse(
    localStorage.getItem("root-exploration-data"),
  );
  exploration_data[current_location] = {
    npcs: npcs,
    gates: gates,
    scavenge: scavenge,
  };

  localStorage.setItem(
    "root-exploration-data",
    JSON.stringify(exploration_data),
  );
}

function display_exploration_data() {
  const locations = document.querySelectorAll(".job-name:not([processed])");
  if (locations.length == 0) {
    setTimeout(display_exploration_data, 1000);
    return;
  }

  const exploration_data = JSON.parse(
    localStorage.getItem("root-exploration-data"),
  );

  if (!exploration_data) {
    return;
  }

  for (const location of locations) {
    const locationName = location.textContent.trim();
    console.log(locationName);
    if (!exploration_data[locationName]) {
      continue;
    }

    const locationData = exploration_data[locationName];

    // Create the main container
    const container = document.createElement("div");
    container.className = "exploration-data-container";

    // Process NPCs
    container.appendChild(createNPCSection(locationData.npcs, locationName));

    // Process Gates
    container.appendChild(createGatesSection(locationData.gates, locationName));

    // Process Scavenge Points
    //container.appendChild(
    //  createScavengeSection(locationData.scavenge, locationName),
    //);

    // Append the container to the parent
    const parent = $(location).parents("div[class^='job-cont']")[0];
    $(parent).append(container);
    location.setAttribute("processed", true);
  }
}

function createNPCSection(npcs, locationName) {
  const section = document.createElement("div");

  // Check if NPCs exist and determine if they're in array or object format
  if (
    !npcs ||
    (typeof npcs === "object" && Object.keys(npcs).length === 0) ||
    (Array.isArray(npcs) && npcs.length === 0)
  ) {
    const noNPCs = document.createElement("p");
    noNPCs.textContent = "No NPCs available";
    section.appendChild(noNPCs);
    return section;
  }

  // Create table
  const table = document.createElement("table");

  // Create table header
  const headerRow = document.createElement("tr");
  const headers = ["Name", "Life", "Max Life", "Respawn Time"];

  // Use "Index" instead of "ID" for array-based data
  if (Array.isArray(npcs)) {
    headers[0] = "Index";
  }

  headers.forEach((headerText) => {
    const th = document.createElement("th");
    th.textContent = headerText;
    headerRow.appendChild(th);
  });

  table.appendChild(headerRow);

  // Create table rows
  // Handle object format (like in Fuel Depot)
  for (const id in npcs) {
    const npc = npcs[id];
    const row = document.createElement("tr");

    // Name
    const nameCell = document.createElement("td");
    nameCell.textContent = npc.name;
    row.appendChild(nameCell);

    // Life
    const lifeCell = document.createElement("td");
    lifeCell.textContent = npc.life;
    row.appendChild(lifeCell);

    // Max Life
    const maxLifeCell = document.createElement("td");
    maxLifeCell.textContent = npc.max_life;
    row.appendChild(maxLifeCell);

    // Respawn Time
    const respawnCell = document.createElement("td");
    const timerDiv = document.createElement("div");
    timerDiv.className = "timer";
    if (npc.respawn_time) {
      const currentTime = new Date();
      timerDiv.timeLeft = Math.round(
        (npc.respawn_time - currentTime.getTime()) / 1000,
      );
      timerDiv.innerText = getDayHourMinute(timerDiv.timeLeft);
      respawnCell.appendChild(timerDiv);
    } else {
      respawnCell.textContent = "N/A";
    }
    row.appendChild(respawnCell);

    table.appendChild(row);
  }

  section.appendChild(table);
  return section;
}

function createGatesSection(gates, locationName) {
  const section = document.createElement("div");

  // Check if gates exist and determine if they're in array or object format
  if (
    !gates ||
    (typeof gates === "object" && Object.keys(gates).length === 0) ||
    (Array.isArray(gates) && gates.length === 0)
  ) {
    const noGates = document.createElement("p");
    noGates.textContent = "No gates available";
    section.appendChild(noGates);
    return section;
  }

  // Create table
  const table = document.createElement("table");

  // Create table header
  const headerRow = document.createElement("tr");
  const headers = ["Name", "Unlocked", "Gate Unlock Time", "Required Items"];

  // Use "Index" instead of "ID" for array-based data
  if (Array.isArray(gates)) {
    headers[0] = "Index";
  }

  headers.forEach((headerText) => {
    const th = document.createElement("th");
    th.textContent = headerText;
    headerRow.appendChild(th);
  });

  table.appendChild(headerRow);

  // Create table rows
  // Handle object format
  for (const id in gates) {
    const gate = gates[id];
    const row = document.createElement("tr");

    // Name
    const nameCell = document.createElement("td");
    nameCell.textContent = gate.name;
    row.appendChild(nameCell);

    // Unlocked
    const unlockedCell = document.createElement("td");
    unlockedCell.textContent = gate.unlocked ? "Yes" : "No";
    row.appendChild(unlockedCell);

    // Gate Unlock Time
    const unlockTimeCell = document.createElement("td");
    const timerDiv = document.createElement("div");
    timerDiv.className = "timer";
    if (gate.gate_unlock_time) {
      const currentTime = new Date();
      timerDiv.timeLeft = Math.round(
        (gate.gate_unlock_time - currentTime.getTime()) / 1000,
      );
      timerDiv.innerText = getDayHourMinute(timerDiv.timeLeft);
      unlockTimeCell.appendChild(timerDiv);
    } else {
      unlockTimeCell.textContent = "N/A";
    }
    row.appendChild(unlockTimeCell);

    // Required Items
    const requiredItemsCell = document.createElement("td");
    if (gate.required_items && Object.keys(gate.required_items).length > 0) {
      const requiredItems = [];
      for (const item in gate.required_items) {
        requiredItems.push(`${item} (${gate.required_items[item]})`);
      }
      requiredItemsCell.textContent = requiredItems.join(", ");
    } else {
      requiredItemsCell.textContent = "None";
    }
    row.appendChild(requiredItemsCell);

    table.appendChild(row);
  }

  section.appendChild(table);
  return section;
}

function createScavengeSection(scavengePoints, locationName) {
  const section = document.createElement("div");

  // Check if scavenge points exist and determine if they're in array or object format
  if (
    !scavengePoints ||
    (typeof scavengePoints === "object" &&
      Object.keys(scavengePoints).length === 0) ||
    (Array.isArray(scavengePoints) && scavengePoints.length === 0)
  ) {
    const noScavenge = document.createElement("p");
    noScavenge.textContent = "No scavenge points available";
    section.appendChild(noScavenge);
    return section;
  }

  // Create table
  const table = document.createElement("table");

  // Create table header
  const headerRow = document.createElement("tr");
  const headers = ["ID", "Name", "Cooldown", "Cooldown End"];

  // Use "Index" instead of "ID" for array-based data
  if (Array.isArray(scavengePoints)) {
    headers[0] = "Index";
  }

  headers.forEach((headerText) => {
    const th = document.createElement("th");
    th.textContent = headerText;
    headerRow.appendChild(th);
  });

  table.appendChild(headerRow);

  // Create table rows
  // Handle object format
  for (const id in scavengePoints) {
    const point = scavengePoints[id];
    const row = document.createElement("tr");

    // ID
    const idCell = document.createElement("td");
    idCell.textContent = id;
    row.appendChild(idCell);

    // Name
    const nameCell = document.createElement("td");
    nameCell.textContent = point.name;
    row.appendChild(nameCell);

    // Cooldown
    const cooldownCell = document.createElement("td");
    cooldownCell.textContent = point.cooldown || "N/A";
    row.appendChild(cooldownCell);

    // Cooldown End
    const cooldownEndCell = document.createElement("td");
    if (point.cooldown_end && point.cooldown_end > 0) {
      cooldownEndCell.textContent = new Date(
        point.cooldown_end,
      ).toLocaleString();
    } else {
      cooldownEndCell.textContent = "N/A";
    }
    row.appendChild(cooldownEndCell);

    table.appendChild(row);
  }

  section.appendChild(table);
  return section;
}

function addOtherTimersDropDown(dataContainer) {
  let arrowMenu = document.getElementById("arrow-menu");
  if (arrowMenu) {
    arrowMenu.remove();
  }

  const currentTime = new Date();
  arrowMenu = document.createElement("div");
  arrowMenu.id = "arrow-menu";
  arrowMenu.className = "arrow-menu";
  arrowMenu.style.cursor = "pointer";
  arrowMenu.innerHTML = "<span id='arrow'>▼</span>";

  menuOptions = document.createElement("div");
  menuOptions.className = "menu-options";
  menuOptions.id = "menu-options";
  menuOptions.style.display = "none";

  const exploration_data = JSON.parse(
    localStorage.getItem("root-exploration-data"),
  );

  for (const location in exploration_data) {
    const locationData = exploration_data[location];
    const scavenge = locationData.scavenge || {};
    if (scavenge) {
      for (const scavengeD in scavenge) {
        const scavengeData = scavenge[scavengeD];
        let timeLeft = Math.round(
          (scavengeData.cooldown_end - currentTime.getTime()) / 1000,
        );
        if (timeLeft < 0) {
          timeLeft = -1;
        }

        const option = document.createElement("div");
        option.className = "menu-item";
        option.textContent = scavengeData.name;
        const scavengeNameSpacesRemoved = scavengeData.name.replaceAll(
          " ",
          "_",
        );
        option.addEventListener("click", function () {
          if (LOCATION_EXPLORATION_ID[location]) {
            //console.log("Clicked on ", location);
            window.location.href = `/explore/${LOCATION_EXPLORATION_ID[location]}`;
          }
        });
        const timer = document.createElement("div");
        timer.className = "timer menu-item-timer";
        timer.id = scavengeNameSpacesRemoved + "TimeLeft";
        timer.timeLeft = timeLeft;
        timer.innerText = getDayHourMinute(timer.timeLeft);
        option.appendChild(timer);
        menuOptions.appendChild(option);
      }
    }
  }
  arrowMenu.addEventListener("click", function () {
    const isVisible = menuOptions.style.display === "block";
    menuOptions.style.display = isVisible ? "none" : "block";
    document.getElementById("arrow").innerHTML = isVisible ? "▼" : "▲";
  });

  arrowMenu.appendChild(menuOptions);
  dataContainer.appendChild(arrowMenu);
}

function handleStartJob(response) {
  console.log("Handling Start Job");
  if (response.error) {
    return;
  }
  const job_codename = response.job.codename;
  if (
    job_codename.startsWith("job_fuel_depot_fuel_trader") ||
    job_codename.startsWith("job_vault_lockbox") ||
    job_codename.startsWith("job_demolition_site")
  ) {
    handleExplorationTrade(response);
  }
}

function handleExplorationTrade(response) {
  console.log("Handling Exploration Trade");
  const exploration_data = JSON.parse(
    localStorage.getItem("root-exploration-data"),
  );

  const currentTime = new Date();
  const job_name = response.job.name;
  const job_id = response.job.id;
  const wait_time = response.job.vars.cooldown;
  const wait_time_end = currentTime.getTime() + wait_time * 1000;

  exploration_data[current_location].scavenge[job_id] = {
    name: job_name,
    cooldown: wait_time,
    cooldown_end: wait_time_end,
  };
  localStorage.setItem(
    "root-exploration-data",
    JSON.stringify(exploration_data),
  );
  updateDisplay();
}

function handleWeaponDurability(response) {
  console.log("Handling Weapon Durability");
  const weaponDurabilityNew = response.equip?.primary?.vars?.condition || -1;
  weaponDurability = weaponDurabilityNew * 1;
}

function checkWeaponDurability() {
  console.log("Checking Weapon Durability");
  if (weaponDurability == -1) {
    showWarning("Weapon is not equipped!");
  } else if (weaponDurability - 30 < 0) {
    showWarning("Weapon is low on durability!");
  }
}

function showWarning(message) {
  console.log("Showing Warning");
  if ($(".warning").length > 0 && $(".warning").text() == message) return;
  const warningDiv = document.createElement("div");
  warningDiv.className = "warning";
  warningDiv.innerText = message;
  warningDiv.style.opacity = 0; // Initially hide the element
  warningDiv.style.bottom = "-10px"; // Position it at the bottom
  document.body.appendChild(warningDiv);
  setTimeout(() => {
    warningDiv.style.bottom = "35px"; // Animate it to the top
    warningDiv.style.opacity = "1"; // Show it
    setTimeout(function () {
      removeWarning(warningDiv);
    }, 5000); // Remove it after 5 seconds
  }, 200);
}

function removeWarning(warningDiv) {
  warningDiv.style.bottom = "-50px";
  warningDiv.style.opacity = "0";

  setTimeout(() => {
    warningDiv.remove();
  }, 1000);
}

function checkWeaponDurabilityChange(response) {}

function getExpectedNUmberOfHuntsWithDurability(response) {}

async function getLatestLoadItems() {
  console.log("Getting Latest Load Items");
  const response = $.getJSON({
    url: "https://api.zed.city/loadItems",
    xhrFields: { withCredentials: true },
  });
  return response;
}

async function getUserSearch(query) {
  console.log("Getting User Search");
  const response = await $.ajax({
    url: "https://api.zed.city/getUsers",
    type: "POST",
    contentType: "application/json",
    data: JSON.stringify({
      descending: false,
      filter: query,
      page: 1,
    }),
    dataType: "json",
    xhrFields: { withCredentials: true },
  });
  return response.users;
}

function addSearchBar() {
  console.log("Adding Search Bar");
  const parent = document.querySelector("div.q-gutter-xs:nth-child(5)");

  if (!parent) {
    setTimeout(addSearchBar, 100);
    return;
  }
  // Create overlay background
  const overlay = document.createElement("div");
  overlay.className = "search-overlay";
  Object.assign(overlay.style, {
    position: "fixed",
    top: "0",
    left: "0",
    width: "100%",
    height: "100%",
    backgroundColor: "rgba(0, 0, 0, 0.7)",
    display: "none",
    justifyContent: "center",
    alignItems: "center",
    zIndex: "100",
  });

  // Create search container
  const searchContainer = document.createElement("div");
  searchContainer.className = "search-container";
  Object.assign(searchContainer.style, {
    width: "50%",
    maxWidth: "500px",
    backgroundColor: "rgba(9, 10, 11, 0.8)",
    borderRadius: "8px",
    padding: "20px",
    boxShadow: "0 4px 8px rgba(0, 0, 0, 0.2)",
  });

  // Create search bar
  const searchBar = document.createElement("input");
  searchBar.type = "text";
  searchBar.placeholder = "User Search";
  searchBar.className = "search-bar";
  searchBar.disabled = true;
  Object.assign(searchBar.style, {
    width: "100%",
    padding: "10px",
    fontSize: "16px",
    border: "1px solid #ccc",
    borderRadius: "4px",
    marginBottom: "10px",
  });

  const switchModeButton = document.createElement("button");
  switchModeButton.innerText = "Switch " + TOGGLE;
  switchModeButton.className = "search-bar-button";
  Object.assign(switchModeButton.style, {
    color: "white",
    background: "none",
    border: "none",
    borderRadius: "4px",
    cursor: "pointer",
    marginLeft: "10px",
  });
  searchContainer.appendChild(switchModeButton);

  // Create results container
  const resultsContainer = document.createElement("div");
  resultsContainer.className = "search-bar-results";
  Object.assign(resultsContainer.style, {
    maxHeight: "300px",
    overflowY: "auto",
  });

  // Create results list
  const ul = document.createElement("ul");
  ul.className = "search-bar-results-list";
  Object.assign(ul.style, {
    listStyle: "none",
    padding: "0",
    margin: "0",
  });
  // Create search button and icon
  const searchIcon = document.createElementNS(
    "http://www.w3.org/2000/svg",
    "svg",
  );
  searchIcon.setAttribute("width", "20");
  searchIcon.setAttribute("height", "20");
  searchIcon.setAttribute("viewBox", "0 0 60 60");
  searchIcon.innerHTML = `
    <circle cx="30" cy="30" r="20" fill="none" stroke="currentColor" stroke-width="4"/>
    <path d="M45 45 L60 60" stroke="currentColor" stroke-width="4" stroke-linecap="round"/>
  `;
  const searchButton = document.createElement("button");
  searchButton.className = "search-bar-button";
  searchButton.appendChild(searchIcon);
  Object.assign(searchButton.style, {
    color: "white",
    background: "none",
    border: "none",
    borderRadius: "4px",
    cursor: "pointer",
  });

  // Assemble the components
  resultsContainer.appendChild(ul);
  searchContainer.appendChild(searchBar);
  searchContainer.appendChild(resultsContainer);
  overlay.appendChild(searchContainer);
  document.body.appendChild(overlay);

  // Add event listeners
  searchBar.addEventListener("input", function () {
    if (searchBar.disabled) return;
    const query = searchBar.value.toLowerCase();
    if (query.length > 0) {
      const searchMode = searchBar.getAttribute("searchMode") || "users";
      //console.log(searchMode);
      if (searchMode === "users") {
        // Changed from > 3 to > 0 to show results immediately
        console.log("Searching users");
        getUserSearch(query).then((response) => {
          parseUsers(response, ul);
        });
      } else if (searchMode === "market") {
        console.log("Searching market");
        marketSearch(query, ul);
      }
    }
  });

  searchButton.addEventListener("click", function () {
    searchBar.disabled = false;
    searchBar.value = "";
    overlay.style.display = "flex";
    searchBar.focus(); // Automatically focus the search input
  });
  // Close when clicking outside the search container
  overlay.addEventListener("click", function (event) {
    if (event.target === overlay) {
      overlay.style.display = "none";
      searchBar.value = "";
      ul.innerHTML = "";
      searchBar.disabled = true;
    }
  });

  switchModeButton.addEventListener("click", function () {
    const currentMode = searchBar.getAttribute("searchMode") || "users";
    if (currentMode === "users") {
      searchBar.setAttribute("searchMode", "market");
      searchBar.placeholder = "Market Search";
      searchBar.value = "";
      ul.innerHTML = "";
    } else {
      searchBar.setAttribute("searchMode", "users");
      searchBar.placeholder = "User Search";
      searchBar.value = "";
      ul.innerHTML = "";
    }
  });

  // prepend search button to the parent element
  parent.prepend(searchButton);
}

function parseUsers(users, ul) {
  console.log("Parsing Users");
  // Clear previous results
  ul.innerHTML = "";

  if (users.length === 0) {
    // Display a message when no users are found
    const noResults = document.createElement("li");
    noResults.className = "no-results";
    noResults.innerText = "No users found";
    Object.assign(noResults.style, {
      padding: "12px",
      textAlign: "center",
      color: "#666",
      borderBottom: "1px solid #eee",
    });
    ul.appendChild(noResults);
    return;
  }

  // Create result items for each user
  for (const user of users) {
    const li = document.createElement("li");
    li.className = "user-result-item";
    Object.assign(li.style, {
      padding: "10px",
      borderBottom: "1px solid #eee",
      cursor: "pointer",
      transition: "background-color 0.2s",
    });

    const a = document.createElement("a");
    a.href = `/profile/${user.id}`;
    a.innerText = user.username;
    Object.assign(a.style, {
      textDecoration: "none",
      color: "white",
      display: "block",
      width: "100%",
      height: "100%",
    });

    // Add hover effect
    li.addEventListener("mouseover", function () {
      this.style.backgroundColor = "#333";
    });

    li.addEventListener("mouseout", function () {
      this.style.backgroundColor = "transparent";
    });

    li.appendChild(a);
    ul.appendChild(li);
  }
}

addSearchBar();

// add trade value to kitchen
//  - ammo bench
//  - weapon bench
//  - crafting bench
//  - armor bench

const recipes = {};
function handlegetRecipes(response) {
  console.log("Handling Recipes");
  for (const recipe of response.items) {
    const recipeName = recipe.name.replace(" Blueprint", "");
    const unlocked = recipe.vars.has_recipe;
    if (!unlocked) {
      continue;
    }

    const outputs = [];
    const items_required = [];

    if (recipe.vars?.items) {
      for (const itemr in recipe.vars.items) {
        const item = recipe.vars.items[itemr];
        const item_name = item.name;
        const item_qty = item.req_qty;

        const obj = {
          name: item_name,
          quantity: item_qty,
        };
        items_required.push(obj);
      }
    }

    if (recipe.vars?.output) {
      for (const itemr in recipe.vars.output) {
        const item = recipe.vars.output[itemr];
        const item_name = item.name;
        const item_qty = item.quantity;

        const obj = {
          name: item_name,
          quantity: item_qty,
        };
        outputs.push(obj);
      }
    }

    recipes[recipeName] = {
      items_required: items_required,
      outputs: outputs,
    };
  }

  if (window.location.href.includes("1781367")) {
    // radio tower
    setTimeout(displayRadioTowerValues, 500);
  } else {
    setTimeout(displayRecipeValues, 500);
  }
}

function displayRecipeValues() {
  console.log("Display Recipes Values");
  const itemsDiv = document.querySelectorAll(".q-item__label");
  if (itemsDiv.length == 0) {
    if (window.location.href.includes("/stronghold/")) {
      setTimeout(displayRecipeValues, 500);
    }
    return;
  }

  itemsDiv.forEach((item) => {
    const itemName = item.innerText;
    if (recipes[itemName]) {
      const [cost, output, profit] = calculateCostOutput(recipes[itemName]);

      const innerHTML = `${cost.toLocaleString()} → ${output.toLocaleString()}  [${profit.toLocaleString()}]`;
      const profitDiv = document.createElement("div");
      profitDiv.innerHTML = innerHTML;
      profitDiv.style.color = profit > 0 ? "green" : "red";
      profitDiv.className = "profit-div";
      item.appendChild(profitDiv);
    }
  });
}

function calculateCostOutput(recipe) {
  console.log("Calculating Cost Output");
  const items_required = recipe.items_required;
  const outputs = recipe.outputs;
  let cost = 0;
  let output = 0;

  for (const item of items_required) {
    const itemName = item.name;
    const itemQuantity = item.quantity;
    const price = market_data[itemName] || 0;
    cost += price * itemQuantity;
  }

  for (const item of outputs) {
    const itemName = item.name;
    const itemQuantity = item.quantity || 1;
    const price = market_data[itemName] || 0;
    output += price * itemQuantity;
  }
  const profit = output - cost;
  return [cost, output, profit];
}

const radioTowerTrades = {};
function displayRadioTowerValues() {
  console.log("Displaying Radio Tower Values");
  //console.log(radioTowerTrades);

  const columns = getTradeContainers();
  if (columns.length == 0) {
    if (window.location.href.includes("/stronghold/")) {
      setTimeout(displayRadioTowerValues, 500);
      return;
    }
  }
  //console.log(columns);
  if (columns.length == 0) {
    return;
  }

  for (let columnNo = 0; columnNo < columns.length; columnNo++) {
    const column = columns[columnNo];
    //console.log(column);
    const tradeName = "Trade " + columnNo;
    if (radioTowerTrades[tradeName]) {
      const [cost, output, profit] = calculateCostOutput(
        radioTowerTrades[tradeName],
      );

      const innerHTML = `${cost.toLocaleString()} → ${output.toLocaleString()}  [${profit.toLocaleString()}]`;
      const profitDiv = document.createElement("div");
      profitDiv.innerHTML = innerHTML;
      profitDiv.style.color = profit > 0 ? "green" : "red";
      profitDiv.style.position = "absolute";
      profitDiv.className = "profit-div";
      column.appendChild(profitDiv);
    }
  }
}

function getTradeContainers() {
  const containers = document.querySelectorAll("button");
  const relevantContainers = [];

  containers.forEach((container) => {
    if (container.innerText.includes("TRADE")) {
      const parent = container.parentElement;
      relevantContainers.push(parent);
    }
  });
  return relevantContainers;
}

function handleRadioTowerValues(response) {
  console.log("Handling Radio Tower Values");
  let index = 0;
  for (const trade of response.items) {
    const tradeName = "Trade " + index;

    const outputs = [];
    const items_required = [];

    if (trade.vars?.items) {
      for (const itemr in trade.vars.items) {
        const item = trade.vars.items[itemr];
        const item_name = item.name;
        const item_qty = item.req_qty;

        const obj = {
          name: item_name,
          quantity: item_qty,
        };
        items_required.push(obj);
      }
    }

    if (trade.vars?.output) {
      for (const itemr in trade.vars.output) {
        const item = trade.vars.output[itemr];
        const item_name = item.name;
        const item_qty = item.quantity;

        const obj = {
          name: item_name,
          quantity: item_qty,
        };
        outputs.push(obj);
      }
    }

    radioTowerTrades[tradeName] = {
      items_required: items_required,
      outputs: outputs,
    };
    index++;
  }

  setTimeout(displayRadioTowerValues, 500);
}

function checkVehicleInventoryWeight() {
  const vehicleInventoryWeight = data.vehicle?.vars?.weight || 0;
  if (vehicleInventoryWeight - 12 > 0) {
    showWarning("Vehicle Inventory is high!");
  }
}

function sortBySimilarity(referenceString, stringList) {
  function calculateSimilarity(s1, s2) {
    s1 = s1.toLowerCase();
    s2 = s2.toLowerCase();

    if (s2.startsWith(s1) || s1.startsWith(s2)) {
      return (
        1 -
        Math.abs(s1.length - s2.length) / Math.max(s1.length, s2.length) +
        0.5
      ); // Stronger weight for prefix
    }

    const shorter = s1.length < s2.length ? s1 : s2;
    const longer = s1.length < s2.length ? s2 : s1;

    const words = longer.split(/\s+/);
    const initials = words.map((word) => word[0]).join("");

    if (shorter.length <= initials.length && initials.includes(shorter)) {
      return 0.9; // Increased weight for matching initials
    }

    function levenshteinDistance(a, b) {
      if (a.length < b.length) return levenshteinDistance(b, a);
      if (b.length === 0) return a.length;
      let previousRow = Array.from({ length: b.length + 1 }, (_, i) => i);
      for (let i = 0; i < a.length; i++) {
        let currentRow = [i + 1];
        for (let j = 0; j < b.length; j++) {
          const insertions = previousRow[j + 1] + 1;
          const deletions = currentRow[j] + 1;
          const substitutions = previousRow[j] + (a[i] !== b[j] ? 1 : 0);
          currentRow.push(Math.min(insertions, deletions, substitutions));
        }
        previousRow = currentRow;
      }
      return previousRow[previousRow.length - 1];
    }

    const maxLength = Math.max(s1.length, s2.length);
    const distance = levenshteinDistance(s1, s2);
    return 1 - distance / maxLength;
  }

  return [...stringList].sort(
    (a, b) =>
      calculateSimilarity(referenceString, b) -
      calculateSimilarity(referenceString, a),
  );
}

function marketSearch(query, ul) {
  const sortedResults = sortBySimilarity(query, Object.keys(market_data)).slice(
    0,
    5,
  );
  //console.log(sortedResults);
  parseMarketSearchData(sortedResults, ul);
}

function parseMarketSearchData(marketList, ul) {
  console.log("Parsing marketList");
  //console.log(marketList);
  // Clear previous results
  ul.innerHTML = "";

  if (marketList.length === 0) {
    // Display a message when no marketList are found
    const noResults = document.createElement("li");
    noResults.className = "no-results";
    noResults.innerText = "No marketList found";
    Object.assign(noResults.style, {
      padding: "12px",
      textAlign: "center",
      color: "#666",
      borderBottom: "1px solid #eee",
    });
    ul.appendChild(noResults);
    return;
  }

  for (const marketItem of marketList) {
    const marketPrice = market_data[marketItem];

    //console.log(marketItem, marketPrice);
    const li = document.createElement("li");
    li.className = "user-result-item";
    Object.assign(li.style, {
      padding: "10px",
      borderBottom: "1px solid #eee",
      cursor: "pointer",
      transition: "background-color 0.2s",
    });

    const a = document.createElement("a");
    a.href = `/market`;
    a.innerText = marketItem;
    Object.assign(a.style, {
      textDecoration: "none",
      color: "white",
      display: "block",
      width: "100%",
      height: "100%",
    });

    const priceDiv = document.createElement("div");
    priceDiv.innerText = `$${marketPrice.toLocaleString()}`;
    priceDiv.style.float = "right";
    a.appendChild(priceDiv);

    // Add hover effect
    li.addEventListener("mouseover", function () {
      this.style.backgroundColor = "#333";
    });

    li.addEventListener("mouseout", function () {
      this.style.backgroundColor = "transparent";
    });

    li.appendChild(a);
    ul.appendChild(li);
  }
}