import { Job } from "@enact/core/util";
import Enact_$L from "@enact/i18n/$L";

import stringReSourceDe from "../../resources/de/strings.json";
import stringReSourceEn from "../../resources/en/strings.json";
import stringReSourceGb from "../../resources/gb/strings.json";
import stringReSourceRu from "../../resources/ru/strings.json";
import { getRicCode } from "../api/apiConfig";
import { ERROR_MESSAGES_GROUPS, SECRET_KEY } from "./Config";

let _boundingRectCache = {};
const BOUNDING_RECT_IGNORE_TIME = 10;

const generateUUID = () => {
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
    var r = (Math.random() * 16) | 0,
      v = c === "x" ? r : (r & 0x3) | 0x8;
    return v.toString(16);
  });
};

/**
 * prevent multiple call
 * @param {*} node
 * @returns
 */
const clearBoundingRectCache = new Job(() => {
  _boundingRectCache = {};
}, 5000);

export const getBoundingClientRect = (node) => {
  clearBoundingRectCache.start();
  if (!node.dataset.uuid) {
    node.dataset.uuid = generateUUID();
  }

  const uuid = node.dataset.uuid;

  if (_boundingRectCache[uuid]) {
    if (
      Date.now() - _boundingRectCache[uuid].called <
      BOUNDING_RECT_IGNORE_TIME
    ) {
      return _boundingRectCache[uuid].boundingRect;
    }
  }

  const boundingRect = node.getBoundingClientRect();

  _boundingRectCache[uuid] = {
    boundingRect: boundingRect?.toJSON(),
    called: Date.now(),
  };

  return boundingRect;
};

const stringReSource = {
  US: stringReSourceEn,
  GB: stringReSourceGb,
  DE: stringReSourceDe,
  RU: stringReSourceRu,
};

export const $L = (str) => {
  let languageSetting = "system";
  let resourceKey = "";

  if (typeof window === "object" && window.store) {
    languageSetting = window.store.getState().localSettings.languageSetting;
    if (languageSetting === "system") {
      resourceKey = window.store.getState().common.httpHeader?.cntry_cd;
    } else {
      resourceKey = languageSetting;
    }
  }
  const resource = stringReSource[resourceKey];
  if (typeof str === "object") {
    if (resource && resource[str.key]) {
      return resource[str.key].replace(/{br}/g, "{br}");
    } else {
      return str.value;
    }
  } else if (resource && resource[str]) {
    return resource[str].replace(/{br}/g, "{br}");
  }
  return str && str.replace(/{br}/g, "{br}");
};

export const createQueryString = (object) => {
  const parts = [];
  for (const key of Object.getOwnPropertyNames(object)) {
    if (
      object[key] !== null &&
      object[key] !== undefined &&
      object[key] !== ""
    ) {
      parts.push(`${key}=${encodeURIComponent(object[key])}`);
    }
  }
  return parts.join("&");
};

export const wait = (time) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, time);
  });
};

export const scaleW = (value) => {
  if (typeof window === "object") {
    return value * (window.innerWidth / 1920);
  }
  return value;
};

export const scaleH = (value) => {
  if (typeof window === "object") {
    return value * (window.innerHeight / 1080);
  }
  return value;
};

//for test

let localLaunchParams = {
  // contentTarget: "V3_8001_Tv_PD_1_A639507_0_766",
  // contentTarget: "V3_8001_Tv_LS_1_USQVC_0_766",
  // contentTarget: "V3_8001_Tv_VS_1_6643524e649d1d4bf0ad523f_0_766",
  // contentTarget: "V3_8001_Tv_TD_11_820_3705018",
  // contentTarget: "V3_8001_Tv_HD_7_527_0",
  // contentTarget: "V3_8001_Tv_HP_7_527_0",
  // contentTarget: "V3_8001_Tv_WE_twin",
  // contentTarget: "V3_8001_Tv_OS_1006_Home",
  // contentTarget: "V3_8001_Tv_BS",
  // contentTarget: "V3_8001_Tv_PS",
  // contentTarget: "V3_8001_Tv_SC_1000_Fashion_Item",
  // contentTarget: "V3_8001_Tv_FB_1",
  // contentTarget: "V3_8001_Tv_TC_824",
  // contentTarget: "V3_2000_HOMEBANNER:TOP_MT_1",
  // contentTarget: "V3_3000_AD:TM_TM_8",
  // contentTarget: "V3_3000_AD:SR_SR_1",
  // contentTarget: "V3_2006_HOMEBANNER:6241018_HP_1_1307_2",
  // contentTarget: "V3_2004_HOMEBANNER:4241101_HP_9_889",
  // contentTarget: "V3_2001_HOMEBANNER:1240712_TM_10",
};

export const getLaunchParams = () => {
  let params = {};

  if (
    typeof window === "object" &&
    window.PalmSystem &&
    window.PalmSystem.launchParams
  ) {
    try {
      params = JSON.parse(window.PalmSystem.launchParams);
      if (params["x-webos-app-container-launch"] === true) {
        params = params.details;
      }
    } catch (e) {
      params = {};
    }
    return params;
  } else {
    return localLaunchParams;
  }
};

export const clearLaunchParams = () => {
  console.log("common.clearLaunchParams");
  if (
    typeof window === "object" &&
    window.PalmSystem &&
    window.PalmSystem.launchParams
  ) {
    window.PalmSystem.launchParams = "";
  } else {
    localLaunchParams = {};
  }
};

export const readLocalStorage = (key, defaultValue) => {
  const value = typeof window === "object" && window.localStorage.getItem(key);
  if (!value && defaultValue !== undefined) {
    return defaultValue;
  }
  return value === "undefined" ? null : JSON.parse(value);
};

export const writeLocalStorage = (key, value) => {
  if (typeof window === "object") {
    window.localStorage.setItem(key, JSON.stringify(value));
  }
};

export const convertToTimeFormat = (timeString, isIncludeDate = false) => {
  const date = new Date(timeString);

  let options = { hour: "numeric", minute: "2-digit", hour12: true };
  let pattern = " ";

  if (isIncludeDate) {
    options = {
      ...options,
      month: "2-digit",
      day: "2-digit",
      year: "numeric",
      hour: "2-digit",
    };
    pattern = ",";
  }

  return new Intl.DateTimeFormat("en-US", options)
    .format(date)
    .replace(pattern, "");
};

export const getTranslate3dValueByDirection = (
  element,
  isHorizontal = true
) => {
  try {
    const transformStyle = window.getComputedStyle(element).transform;

    if (!transformStyle || transformStyle === "none") {
      throw new Error("transfrom style not found");
    }

    const transformMatrix = transformStyle.match(/^matrix\((.+)\)$/);

    let index, value;

    if (transformMatrix) {
      const matrixValues = transformMatrix[1].split(", ");

      index = isHorizontal ? 4 : 5;

      value = parseFloat(Math.abs(matrixValues[index]));
    }

    return value;
  } catch (error) {
    console.error(error.message);
  }
};

export const formatGMTString = (date) => {
  let string = date.toISOString().replace(/T/, " ").replace(/\..+/, "");

  return string;
};

export const getSpottableDescendants = (containerId) => {
  let container = document.querySelector(
    `[data-spotlight-id="${containerId}"]`
  );
  if (container) {
    return container.querySelectorAll('[class*="spottable"]');
  }
  return [];
};

export const isElementInContainer = (
  element,
  container,
  fullyVisible = true
) => {
  // 요소와 컨테이너의 사각형 정보 가져오기
  if (typeof window === "object") {
    const elementRect = getBoundingClientRect(element);
    const containerRect = container
      ? getBoundingClientRect(container)
      : {
          top: 0,
          left: 0,
          bottom: window.innerHeight,
          right: window.innerWidth,
        };

    // 요소가 컨테이너의 가시 영역 안에 있는지 판단
    if (fullyVisible) {
      return (
        elementRect.top >= containerRect.top &&
        elementRect.left >= containerRect.left &&
        elementRect.bottom <= containerRect.bottom &&
        elementRect.right <= containerRect.right
      );
    } else {
      return (
        elementRect.bottom > containerRect.top &&
        elementRect.top < containerRect.bottom &&
        elementRect.right > containerRect.left &&
        elementRect.left < containerRect.right
      );
    }
  } else {
    return false;
  }
};

export const isChildFullyShowing = (parentNode, childNode) => {
  try {
    const parentRect = getBoundingClientRect(parentNode);
    const childRect = getBoundingClientRect(childNode);

    if (
      childRect.top >= parentRect.top &&
      childRect.left >= parentRect.left &&
      childRect.bottom <= parentRect.bottom &&
      childRect.right <= parentRect.right
    ) {
      return true;
    }
  } catch (e) {
    return true;
  }

  return false;
};

export const getRectDiff = (element1, element2) => {
  const element1Rect = getBoundingClientRect(element1);
  const element2Rect = getBoundingClientRect(element2);

  return {
    right: element1Rect.right - element2Rect.right,
    left: element1Rect.left - element2Rect.left,
    top: element1Rect.top - element2Rect.top,
    bottom: element1Rect.bottom - element2Rect.bottom,
  };
};

export const getFormattingCardNo = (cardNumber) => {
  return `${"*".repeat(12)}${cardNumber.slice(-4)}`
    .replace(/(.{4})/g, "$1-")
    .slice(0, -1);
};

export const getQRCodeUrl = ({
  serverHOST,
  serverType,
  index,
  patnrId,
  prdtId,
  dirPurcSelYn = "Y",
  prdtData,
  qrType,
  liveFlag = "Y",
  entryMenu,
  nowMenu,
}) => {
  if (!serverHOST) {
    console.error("getQRCodeUrl: Not Supported, Host is missing");
    return {};
  }

  let sdpURL = serverHOST.split(".")[0];
  let countryCode = "";

  if (sdpURL.indexOf("-") > 0) {
    countryCode = sdpURL.split("-")[1];
  } else {
    countryCode = sdpURL;
  }

  sdpURL = sdpURL.toLowerCase();

  if (serverType !== "system") {
    sdpURL = serverType;
  }

  let baseUrl = "";

  if (sdpURL.indexOf("qt2") >= 0) {
    baseUrl = "https://qt2-m.shoptime.lgappstv.com/";
  } else if (sdpURL.indexOf("qt") >= 0) {
    baseUrl = "https://qt-m.shoptime.lgappstv.com/";
  } else {
    baseUrl = "https://m.shoptime.lgappstv.com/";
  }
  const prdtDataStr = JSON.stringify(prdtData);
  const prdtDataBase64 = btoa(prdtDataStr);

  const encodedNowMenu = encodeURIComponent(nowMenu);
  const encodeEntryMenu = encodeURIComponent(entryMenu);

  return {
    detailUrl: `${baseUrl}billing?cntryCd=${countryCode}&entryMenu=${encodeEntryMenu}&nowMenu=${encodedNowMenu}&idx=${index}&qrType=${qrType}&prdtId=${prdtId}&liveFlag=${liveFlag}&patnrId=${patnrId}`,
    detailBuyNowUrl: `${baseUrl}billing?cntryCd=${countryCode}&qrType=billingBuyNow&entryMenu=${encodeEntryMenu}&nowMenu=${encodedNowMenu}&idx=${index}&liveFlag=${liveFlag}&patnrId=${patnrId}&prdtData=${prdtDataBase64}&dirPurcSelYn=${dirPurcSelYn}&prdtId=${prdtId}`,
    checkoutUrl: `${baseUrl}billing?cntryCd=${countryCode}&qrType=billingCheckOutAddress&entryMenu=${encodeEntryMenu}&nowMenu=${encodedNowMenu}&idx=${index}&liveFlag=${liveFlag}&patnrId=${patnrId}&prdtData=${prdtDataBase64}&dirPurcSelYn=${dirPurcSelYn}&prdtId=${prdtId}`,
    billingAddressListUrl: `${baseUrl}billing?cntryCd=${countryCode}&qrType=billingAddress&entryMenu=${encodeEntryMenu}&nowMenu=${encodedNowMenu}&idx=${index}&from=billing`,
    shippingAddressListUrl: `${baseUrl}billing?cntryCd=${countryCode}&qrType=billingAddress&entryMenu=${encodeEntryMenu}&nowMenu=${encodedNowMenu}&idx=${index}&from=shipping`,
    orderPageUrl: `${baseUrl}my_order.jsp?cntryCd=${countryCode}`,
    homeUrl: `${baseUrl}home.jsp?cntryCd=${countryCode}`,
    setupPinUrl: `${baseUrl}setup_pin.jsp?cntryCd=${countryCode}`,
    cardListUrl: `${baseUrl}billing?cntryCd=${countryCode}&qrType=billingPayment&entryMenu=${encodeEntryMenu}&nowMenu=${encodedNowMenu}&idx=${index}`,
  };
};

// ex: JANUARY 01, 2024
export const getFormattingDate = (dateString) => {
  const date = new Date(dateString.replace(" ", "T"));

  const monthNames = [
    $L("JANUARY"),
    $L("FEBRUARY"),
    $L("MARCH"),
    $L("APRIL"),
    $L("MAY"),
    $L("JUNE"),
    $L("JULY"),
    $L("AUGUST"),
    $L("SEPTEMBER"),
    $L("OCTOBER"),
    $L("NOVEMBER"),
    $L("DECEMBER"),
  ];

  const month = monthNames[date.getMonth()];
  const day = date.getDate().toString().padStart(2, "0");
  const year = date.getFullYear();

  return `${month} ${day}, ${year}`;
};

export const removeSpecificTags = (html) => {
  const tagPatterns = [
    /<a\b[^>]*>(.*?)<\/a>/gi,
    /<script\b[^>]*>(.*?)<\/script>/gi,
    /<iframe\b[^>]*>(.*?)<\/iframe>/gi,
    /<img\b[^>]*\/?>/gi,
    /<link\b[^>]*\/?>/gi,
  ];

  let sanitizedHtml = html;
  tagPatterns.forEach((pattern) => {
    sanitizedHtml = sanitizedHtml.replace(pattern, "");
  });

  return sanitizedHtml;
};

export const encryptPhoneNumber = (phoneNumber) => {
  if (typeof window === "object") {
    return window.CryptoJS.AES.encrypt(phoneNumber, SECRET_KEY).toString();
  }

  return phoneNumber;
};

export const decryptPhoneNumber = (encryptedPhoneNumber) => {
  if (typeof window === "object") {
    const bytes = window.CryptoJS.AES.decrypt(encryptedPhoneNumber, SECRET_KEY);
    return bytes.toString(window.CryptoJS.enc.Utf8);
  }

  return encryptedPhoneNumber;
};

export const formatLocalDateTime = (date) => {
  const isDate = (obj) =>
    Object.prototype.toString.call(obj) === "[object Date]";

  if (typeof window === "object" && isDate(date)) {
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");
    const hours = String(date.getHours()).padStart(2, "0");
    const minutes = String(date.getMinutes()).padStart(2, "0");
    const seconds = String(date.getSeconds()).padStart(2, "0");

    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  }

  return "";
};

export const parseLocalizedNumber = (numberString, countryCode) => {
  // 유럽식: 1.499,00 -> 1499.00
  if (countryCode === "DE") {
    return parseFloat(numberString.replace(/\./g, "").replace(",", "."));
  }
  // 미국식: 1,499.00 -> 1499.00
  if (countryCode === "US" || countryCode === "GB") {
    return parseFloat(numberString.replace(/[^0-9.-]+/g, ""));
  }
  // 러시아식: 1 499,00 -> 1499.00
  if (countryCode === "RU") {
    return parseFloat(numberString.replace(/\s/g, "").replace(",", "."));
  }

  return parseFloat(numberString);
};

export const formatCurrencyValue = (
  value,
  currSign,
  currSignLoc,
  isDiscount = false
) => {
  if (value === "-" || value === 0) return "-";

  const numValue = parseFloat(value);
  if (isNaN(numValue)) return "-";

  const sign = isDiscount && numValue > 0 ? "- " : "";
  const formattedValue = parseFloat(numValue.toFixed(2)).toLocaleString(
    "en-US",
    {
      minimumFractionDigits: 2,
      maximumFractionDigits: 2,
    }
  );

  if (!currSign || !currSignLoc) return `${sign}${formattedValue}`;

  return currSignLoc === "L"
    ? `${sign}${currSign} ${formattedValue}`
    : `${sign}${formattedValue} ${currSign}`;
};

export const getTimeDifferenceByMilliseconds = (
  startTimeString,
  endTimeString,
  threshold = 1000
) => {
  // dateTimePattern: YYYY-MM-DD HH:MM:SS
  const dateTimePattern = /(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/;
  const startMatches = startTimeString.match(dateTimePattern);
  const endMatches = endTimeString.match(dateTimePattern);

  if (!startMatches || !endMatches) {
    return false;
  }

  const convertedStartTime = new Date(startTimeString.replace(/ /, "T") + "Z");
  const convertedEndTime = new Date(endTimeString.replace(/ /, "T") + "Z");
  const timeDifference = convertedEndTime - convertedStartTime;

  return timeDifference > threshold;
};

export const getErrorMessage = (
  errorCode,
  retMsg,
  retDetailCode,
  returnBindStrings
) => {
  const group = ERROR_MESSAGES_GROUPS.find((group) =>
    group.codes.includes(Number(errorCode))
  );

  const errorPrefix = errorCode
    ? retDetailCode
      ? `[${errorCode}-${retDetailCode}] `
      : `[${errorCode}] `
    : "";

  if (group) {
    if (
      errorCode === 1120 &&
      returnBindStrings &&
      typeof returnBindStrings === "object"
    ) {
      return `${errorPrefix} ${group.message} (ID: ${returnBindStrings.join(
        ", "
      )})`;
    }

    return errorPrefix + group.message;
  } else if (retMsg) {
    return errorPrefix + retMsg;
  } else {
    return errorPrefix + "An unknown error occurred. Please try again later.";
  }
};

/**
 * Check if the reservation error is related to system time issues
 * @param {Object} error - The error object from Luna service
 * @returns {boolean} - True if error is time-related
 */
export const isTimeRelatedError = (error) => {
  if (!error) return false;

  const timeErrorPatterns = [
    "Invalid current time",
    "Fail to get Current Time",
    "time not available",
    "clock not set",
  ];

  const errorText = error.errorText || error.message || "";
  return timeErrorPatterns.some((pattern) =>
    errorText.toLowerCase().includes(pattern.toLowerCase())
  );
};

/**
 * Get user-friendly error message for reservation failures
 * @param {Object} error - The error object from Luna service
 * @returns {string} - User-friendly error message
 */
export const getReservationErrorMessage = (error) => {
  if (!error) return $L("Failed to set reminder. Please try again.");

  if (isTimeRelatedError(error)) {
    return $L(
      "Unable to set reminder: System time not available. Please check your TV's time settings and try again."
    );
  }

  if (error.errorText) {
    return $L(`Failed to set reminder: ${error.errorText}`);
  }

  return $L("Failed to set reminder. Please try again.");
};
