const controller = new AbortController();
const signal = controller.signal;

window.screenLock = null;
window.currentLink = document.URL.split("?")[0].split("#")[0];
window.page = window.location.pathname.split("/").pop();

/**
 * Returns the base URL of the current location. If the hostname is "localhost" or
 * an IP address that matches the IPv4 loopback address (127.0.0.1), the protocol and
 * hostname with the first pathname segment are returned. Otherwise, the origin is returned.
 *
 * @return {string} The base URL.
 */
const baseurl = () => {
  const isLocalhost =
    window.location.hostname === "localhost" ||
    window.location.hostname === "[::1]" ||
    window.location.hostname.match(
      /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
    );
  return isLocalhost
    ? `${window.location.protocol}//${window.location.host}/${
        window.location.pathname.split("/")[1]
      }`
    : location.origin;
};

/**
 * An object containing functions that determine if the user's device is running
 * certain mobile operating systems.
 *
 * @type {Object}
 */
const isMobile = {
  /**
   * Returns a boolean indicating whether the user's device is running Android.
   *
   * @return {boolean}
   */
  Android: () => navigator.userAgent.match(/Android/i),

  /**
   * Returns a boolean indicating whether the user's device is running BlackBerry.
   *
   * @return {boolean}
   */
  BlackBerry: () => navigator.userAgent.match(/BlackBerry/i),

  /**
   * Returns a boolean indicating whether the user's device is running iOS.
   *
   * @return {boolean}
   */
  iOS: () => navigator.userAgent.match(/iPhone|iPad|iPod/i),

  /**
   * Returns a boolean indicating whether the user's device is running Opera Mini.
   *
   * @return {boolean}
   */
  Opera: () => navigator.userAgent.match(/Opera Mini/i),

  /**
   * Returns a boolean indicating whether the user's device is running Windows.
   *
   * @return {boolean}
   */
  Windows: () => navigator.userAgent.match(/IEMobile/i),

  /**
   * Returns a boolean indicating whether the user's device is running any of the
   * mobile operating systems checked by the other functions in the `isMobile` object.
   *
   * @return {boolean}
   */
  any: () =>
    isMobile.Android() ||
    isMobile.BlackBerry() ||
    isMobile.iOS() ||
    isMobile.Opera() ||
    isMobile.Windows(),
};

// Function to check internet speed
function checkSpeed() {
  if (navigator.connection) {
    var speed = navigator.connection.downlink;
    console.log(speed + "Mbps");
  } else if (navigator.webkitConnection) {
    var speed = navigator.webkitConnection.downlink;
    console.log(speed + "Mbps");
  } else {
    console.log("Internet speed not supported by browser");
  }
}

// Function to check internet connectivity status
function checkConnectivity() {
  // Check for browser support of addEventListener method
  const logStatus = () => console.log(navigator.onLine ? "Online" : "Offline");

  if (window.addEventListener) {
    window.addEventListener("online", logStatus);
    window.addEventListener("offline", logStatus);
  } else if (window.attachEvent) {
    window.attachEvent("ononline", logStatus);
    window.attachEvent("onoffline", logStatus);
  } else {
    window.ononline = logStatus;
    window.onoffline = logStatus;
  }
}

/**
 * Returns a boolean indicating whether the given string is empty.
 *
 * @param {string} str - The string to check.
 * @return {boolean}
 */
const isEmpty = (str) => {
  return !str || str.length === 0 || str == null || str === "";
};

/**
 * Generates a random string of the specified length.
 *
 * @param {number} length - The length of the string to generate.
 * @return {string}
 */
const randTXT = (length) => {
  let result = "";
  const characters =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  const charactersLength = characters.length;
  for (let i = 0; i < length; i++) {
    result += characters.charAt(Math.floor(Math.random() * charactersLength));
  }
  return result;
};

const randNumbers = (length) => {
  let result = "";
  const characters = "0123456789";
  const charactersLength = characters.length;
  for (let i = 0; i < length; i++) {
    result += characters.charAt(Math.floor(Math.random() * charactersLength));
  }
  return result;
};

/**
 * Adds an event listener to elements with the `check-all-box` class to allow the user to
 * select all checkboxes on the page with a single checkbox. Also enables or disables the
 * `bulk-action` select element and `bulk-action-btn` button element based on whether or not any checkboxes are checked.
 */

document.querySelectorAll(".check-all-box").forEach((el) => {
  el.addEventListener("change", (ev) => {
    const checkboxClass = ev.target.getAttribute("class");

    const _checkboxes = document.querySelectorAll(
      `input[type=checkbox]:not(${checkboxClass})`
    );
    for (let i = 0; i < _checkboxes.length; i++) {
      _checkboxes[i].checked = ev.target.checked;
    }

    const bulk_action = document.querySelector(".bulk-action");

    if (ev.target.checked && bulk_action) {
      bulk_action.disabled = false;
      document.querySelector(".bulk-action-btn").disabled = false;
    } else {
      if (bulk_action) {
        bulk_action.disabled = true;
        document.querySelector(".bulk-action-btn").disabled = true;
      }
    }
  });
});

function currencyInput(txt, evt) {
  var charCode = evt.which ? evt.which : evt.keyCode;

  if (charCode == 46 || charCode == 44) {
    //Check if the text already contains the . or , character
    if (txt.value.indexOf(".") === -1 || txt.value.indexOf(",") === -1) {
      return true;
    } else {
      return false;
    }
  }

  if (charCode > 31 && (charCode < 48 || charCode > 57)) {
    return false;
  }

  if (txt.value.length > 0) {
    var valueInt = removeNonNumeric(txt.value);
    var value = Number(valueInt).toLocaleString();
    txt.value = value;
  }

  return true;
}

function addOptionsToSelect(selectElement, options) {
  for (let option of options) {
    const newOption = new Option(option.text, option.value);
    selectElement.add(newOption);
  }
}

function removeNonNumeric(str) {
  var onlyIntegers = str.replace(/[^0-9]/g, "");
  return parseInt(onlyIntegers);
}

/**
 * Adds event listeners to elements with the `text-length` class to limit the number of
 * characters that can be entered in the element. The number of characters allowed is
 * determined by the element's `data-length` attribute.
 */
document.querySelectorAll(".text-length").forEach((el) => {
  const events = ["change", "keyup", "paste", "blur"];
  for (const event of events) {
    el.addEventListener(event, (ev) => {
      const len = el.getAttribute("data-length");
      el.value = el.value.substr(0, len);
    });
  }
});

/**
 * Returns `true` if the given `value` exists in the given `array`, or `false` otherwise.
 * @param {*} value - The value to search for.
 * @param {Array} array - The array to search in.
 * @returns {boolean} - Whether the value exists in the array.
 */
function isInArray(value, array) {
  return array.indexOf(value) > -1;
}

/**
 * Clears the value of the element with the given `target` selector.
 * @param {string} target - The selector of the element to clear the value of.
 */
function clearValue(target) {
  document.querySelector(target).value = "";
}

function getPercentageDifference(num1, num2) {
  let largerNum = Math.max(num1, num2);
  let smallerNum = Math.min(num1, num2);
  let difference = largerNum - smallerNum;

  return parseFloat((difference / smallerNum) * 100);
}

function readOnly(inputs, action = true) {
  let elem = inputs.elements;
  for (var i = 0, len = elem.length; i < len; ++i) {
    elem[i].readOnly = action;
  }
}

function removeCommas(value) {
  return value.replace(/,/g, "");
}

function isRequiredEmpty(elem) {
  var value_elem =
    !elem.value ||
    elem.value.length === 0 ||
    elem.value == null ||
    elem.value === "";
  if (elem.hasAttribute("required") && value_elem == true) {
    return true;
  }

  return false;
}

/**
 * Returns the value of the URL parameter with the given `sParam` name.
 * If the parameter does not exist, returns `false`.
 * @param {string} sParam - The name of the URL parameter.
 * @returns {string|boolean} - The value of the URL parameter, or `false` if it does not exist.
 */
function urlParam(sParam) {
  let sPageURL = window.location.search.substring(1);
  let sURLVariables = sPageURL.split("&");
  let sParameterName;
  for (let i = 0; i < sURLVariables.length; i++) {
    sParameterName = sURLVariables[i].split("=");
    if (sParameterName[0] === sParam) {
      return typeof sParameterName[1] === "undefined"
        ? true
        : decodeURIComponent(sParameterName[1]);
    }
  }
  return false;
}

function ageRange(from, to) {
  var years = [];
  var from_date = new Date();
  from_date.setFullYear(from_date.getFullYear() - from);
  var _from = from_date.getFullYear();

  var to_date = new Date();
  to_date.setFullYear(to_date.getFullYear() - to);
  var _to = to_date.getFullYear();

  while (_to <= _from) {
    years.push(_to++);
  }
  return years;
}

function removeOptions(selectElement) {
  var i,
    L = selectElement.options.length - 1;
  for (i = L; i >= 0; i--) {
    selectElement.remove(i);
  }
}

function addOptions(container, txt, val) {
  var elem = document.createElement("option");
  elem.textContent = txt;
  elem.value = val;
  container.add(elem);
}

function requiredForm(element) {
  var required_filled = true;
  element.querySelectorAll("[required]")?.forEach(function (input) {
    if (!required_filled) return;
    if (input.type === "radio" || input.type === "checkbox") {
      var radioValueCheck = false;
      element
        .querySelectorAll(`[name='${input.name}']`)
        ?.forEach(function (radio) {
          if (radio.checked) radioValueCheck = true;
        });
      required_filled = radioValueCheck;
      return;
    }
    if (!input.value) {
      required_filled = false;
      return;
    }
  });

  return required_filled;
}

function formData(element) {
  const formParam = new FormData();
  for (let i = 0; i < element.length; i++) {
    if (element[i] && element[i].getAttribute("name")) {
      if (element[i].type === "radio" || element[i].type === "checkbox") {
        if (element[i].checked) {
          formParam.append(element[i].getAttribute("name"), element[i].value);
        }
      } else if (element[i].type === "file") {
        if (element[i].files[0]) {
          formParam.append(
            element[i].getAttribute("name"),
            element[i].files[0],
            element[i].files[0].name
          );
        }
      } else {
        formParam.append(element[i].getAttribute("name"), element[i].value);
      }
    }
  }
  return formParam;
}

async function postForm(url, form, timeout = false) {
  // Create an AbortController instance to allow request cancellation
  const controller = new AbortController();

  // Set timeout if specified
  if (timeout) {
    setTimeout(() => controller.abort(), timeout);
  }

  // Convert form data to URL-encoded query string format
  const formData = new URLSearchParams(new FormData(form));

  try {
    // Send POST request with form data
    const response = await fetch(url, {
      method: "POST",
      headers: {
        "Cache-Control": "no-cache",
        "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
      },
      body: formData,
      credentials: "same-origin",
      cache: "no-cache",
      signal: controller.signal,
    });

    // Check that the response was successful
    if (!response.ok) {
      const message = `An error has occurred: ${response.status}`;
      throw new Error(message);
    }

    // Parse the response body as JSON
    const responseBody = await response.text();
    console.log(responseBody);
    return JSON.parse(responseBody);
  } catch (error) {
    // Handle cancellation error and log other errors
    if (error.name === "AbortError") {
      console.log("Process Abort:");
      hideLoad();
    } else {
      console.log("Fetch error:", error);
    }
  }
}

async function postData(url, body = {}, timeout = 45000) {
  const controller = new AbortController();
  // 20 second timeout:
  const timeoutId = setTimeout(() => controller.abort(), timeout);

  try {
    const response = await fetch(url, {
      method: "POST",
      headers: new Headers({
        Accept: "application/json",
        "Cache-Control": "no-cache",
        "Content-Type": "application/json; charset=UTF-8",
      }),
      body: JSON.stringify(body),
      credentials: "include",
      cache: "no-cache",
      signal: controller.signal,
    });

    if (!response.ok) {
      // const message = `An error has occured: ${response.status}`;
      // throw new Error(message);
      return response.text().then((text) => {
        throw text;

        // if the error is an object and you just want to display some elements:
        //throw JSON.parse(text);
      });
    } else {
      clearTimeout(timeoutId);
      console.log(response);
      return await response.json();
    }
  } catch (error) {
    if (error.name === "AbortError") {
      hideLoad();
    } else {
      console.log("Fetch error: ", error);
    }
  }
}

async function getData(url = "", is_json = true) {
  try {
    const response = await fetch(url, {
      method: "GET",
      mode: "cors", // no-cors, *cors, same-origin
      cache: "no-cache",
      headers: {
        "Cache-Control": "no-cache",
        "Content-Type": "application/json; charset=UTF-8",
      },
      signal: controller.signal,
    });

    if (!response.ok) {
      const message = `An error has occurred: ${response.status}`;
      throw new Error(message);
    }

    if (is_json == true) {
      return await response.json();
    } else {
      return await response.text();
    }
  } catch (error) {
    if (error.name === "AbortError") {
      hideLoad();
    } else {
      console.log("Fetch error: ", error);
    }
  }
}

async function xhrData(
  url = "",
  data,
  is_json = false,
  is_loading = false,
  timeout = false
) {
  try {
    const xhr = new XMLHttpRequest();
    xhr.open("POST", url, true);

    if (is_loading) {
      xhr.upload.onprogress = (event) => {
        if (event.lengthComputable) {
          let percentComplete = (event.loaded / event.total) * 100;
          document.querySelector(
            ".load-percent"
          ).innerHTML = `${percentComplete}%`;
        } else {
          let percentComplete = event.loaded;
          document.querySelector(
            ".load-percent"
          ).innerHTML = `${percentComplete}%`;
        }
      };
    }

    xhr.onerror = (evt) => {
      console.log(`Unable to make request: ${evt}`);
    };

    xhr.onreadystatechange = () => {
      if (xhr.readyState == 3 && is_loading) {
        document.querySelector(".load-percent").innerHTML = "0%";
      }
      if (xhr.readyState !== 4) {
        return;
      }
    };

    if (is_json) {
      xhr.responseType = "json";
      xhr.setRequestHeader("Content-type", "application/json; charset=utf-8");
    }
    xhr.setRequestHeader("Cache-Control", "no-cache");

    if (timeout) {
      xhr.timeout = timeout;
      xhr.ontimeout = function () {
        alert("Timed out!!!");
      };
    }

    xhr.send(data);

    const response = await new Promise((resolve, reject) => {
      xhr.onload = () => {
        if (xhr.status == 200) {
          console.log(xhr.response);
          if (is_json) {
            resolve(xhr.response);
          } else {
            resolve(JSON.parse(xhr.response));
          }
        } else {
          console.log(`Error ${xhr.status}: ${xhr.statusText}`);
          reject(new Error(`Error ${xhr.status}: ${xhr.statusText}`));
        }
      };
    });

    return response;
  } catch (error) {
    console.log(error);
    throw error;
  }
}
