const crypto = require("crypto-js");
const axios = require("axios");
const moment = require("moment");
const OrderForm = require("../models/OrderForm");
const { Op, fn, where, col } = require("sequelize");
const SellerModules = require("../models/SellerModules");
const SellerGroupMembers = require("../models/SellerGroupMembers");
const SellerGroupPermissions = require("../models/SellerGroupPermissions");
const SellerFeatureFlags = require("../models/SellerFeatureFlags");
const SellerControlFeatureFlags = require("../models/SellerControlFeatureFlags");

function generateRandomString() {
  const characters =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  let result = "";
  for (let i = 0; i < 5; i++) {
    result += characters.charAt(Math.floor(Math.random() * characters.length));
  }
  return result;
}

function generateRandomNumber() {
  const min = 10000;
  const max = 99999;
  return Math.floor(Math.random() * (max - min + 1)) + min;
}
function removeSpecialCharactersAndSpaces(str) {
  return str.replace(/[^\w\s]/gi, "").replace(/\s/g, "");
}

function generateSecureRandomSixDigit() {
  const randomValue = Math.random().toString();

  const hashedValue = crypto.SHA256(randomValue);

  const hexString = hashedValue.toString(crypto.enc.Hex);

  const sixDigitOTP = parseInt(hexString.substring(0, 6), 16);

  return sixDigitOTP;
}

async function getCurrentDateTimeInGMT8() {
  try {
    // const response = await axios.get("https://worldtimeapi.org/api/timezone/Asia/Manila");
    // const timeDetail = response.data;
    return new Date();
  } catch (err) {
    console.error(err);
    throw err;
  }
}

function filterAndFormatPhones(phones) {
  return phones
    .filter((phone) => phone && phone !== "")
    .map((phone) => {
      if (phone.startsWith("+63")) {
        phone = phone.slice(1); // Remove the '+'
      } else if (phone.startsWith("0")) {
        phone = "63" + phone.slice(1);
      } else if (phone.startsWith("9")) {
        phone = "63" + phone;
      }
      return phone.length > 12 ? phone.slice(0, 12) : phone;
    });
}

const formatDate12Hour = (date) => {
  const options = {
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit",
    second: "2-digit",
    hour12: true,
  };

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

const formatDateWithoutTime = (date) => {
  const options = {
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
  };

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

const isValidDate = (date) => {
  const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
  if (!dateRegex.test(date)) {
    return false;
  }

  return moment(date, "YYYY-MM-DD", true).isValid();
};

function formatEncodeDate(dateString, isUTC = false) {
  if (dateString) {
    return dateString.toLocaleString("en-US", {
      year: "numeric",
      month: "long",
      day: "numeric",
      hour: "numeric",
      minute: "numeric",
      hour12: true,
      ...(isUTC && { timeZone: "UTC" }),
    });
  } else {
    return dateString;
  }
}

function generateRandomWords(numWords) {
  const words = [];
  const letters = "abcdefghijklmnopqrstuvwxyz";

  function getRandomWord() {
    const wordLength = Math.floor(Math.random() * 7) + 3; // Word length between 3 and 9
    let word = "";
    for (let i = 0; i < wordLength; i++) {
      word += letters.charAt(Math.floor(Math.random() * letters.length));
    }
    return word;
  }

  for (let i = 0; i < numWords; i++) {
    words.push(getRandomWord());
  }

  return words;
}

// const isBase64 = (str) => {
//   if (!str || typeof str !== 'string') return false;

//   // Base64 regex pattern (allows padding with = but not required)
//   const base64Regex = /^[A-Za-z0-9+/]+={0,2}$/;

//   if (!base64Regex.test(str) || str.length % 4 !== 0) {
//     return false; // Not Base64
//   }

//   try {
//     const decoded = Buffer.from(str, 'base64').toString('utf-8');

//     // Ensure decoding doesn't result in binary or garbled text
//     return decoded.trim().length > 0;
//   } catch (err) {
//     return false;
//   }
// };

function isBase64(str) {
  const base64Regex =
    /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{4}|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{2}==)$/;

  return base64Regex.test(str);
}

const filterUpdateValues = (values) => {
  return Object.keys(values).reduce((acc, key) => {
    if (
      values[key] !== null &&
      values[key] !== "" &&
      values[key] !== undefined
    ) {
      acc[key] = values[key];
    }
    return acc;
  }, {});
};

const SELLER_USER_PERMISSIONS_FLAG = "User Permissions";
const SELLER_EXPEDITE_ORDERS = "Expedite Orders";
const SELLER_PURCHASE_ORDERS = "Purchase Orders";

const SELLER_SETTINGS_MODULE = "Settings";
const SELLER_ORDERS_MODULE = "Orders";
const SELLER_VOUCHERS_MODULE = "Vouchers";
const SELLER_PRODUCTS_MODULE = "Products";

const generateReferenceNumber = async (isPaynamicsReference = false) => {
  try {
    const today = moment().format("MMDDYYYY");
    const prefixWithPeriod = `R.O-${today}`;
    const prefixWithoutPeriod = `RO-${today}`;

    const todayRefs = await OrderForm.findAll({
      where: {
        [Op.or]: [
          { reference_no: { [Op.startsWith]: prefixWithPeriod } },
          { reference_no: { [Op.startsWith]: prefixWithoutPeriod } },
        ],
        reference_no: { [Op.notLike]: "%-FORKED-BO" },
      },
      order: [["id", "DESC"]],
      attributes: ["reference_no"],
    });

    let nextSequence = 1;
    if (todayRefs.length > 0) {
      const lastRef = todayRefs[0].reference_no;
      const normalizedRef = lastRef.replace(/^R\.O/, "RO");
      const parts = normalizedRef.split("-");
      if (parts.length === 3) {
        const lastSeq = parseInt(parts[2], 10);
        if (!isNaN(lastSeq)) {
          nextSequence = lastSeq + 1;
        }
      }
    }

    let newRef = `RO-${today}-${String(nextSequence).padStart(2, "0")}`;
    if (isPaynamicsReference) {
      newRef = `RO${today}${String(nextSequence).padStart(2, "0")}`;
    }
    console.log("Generated Reference No:", newRef);
    return newRef;
  } catch (err) {
    console.error("Error generating reference number:", err);
    throw err;
  }
};

async function isUserHasPermissionToDoAction(
  action,
  module,
  seller_account_id,
) {
  if (!["view", "add", "edit", "delete"].includes(action)) {
    throw new Error("Invalid action");
  }

  if (!module || !seller_account_id) return false;

  const sellerModule = await SellerModules.findOne({
    where: where(fn("LOWER", col("name")), module.toLowerCase()),
  });

  if (!sellerModule) return false;

  const sellerGroupMembers = await SellerGroupMembers.findAll({
    where: {
      seller_id: seller_account_id,
      status: true,
    },
  });

  const groupIds = sellerGroupMembers.map((g) => g.seller_group_id);

  const sellerGroupPermission = await SellerGroupPermissions.findOne({
    where: {
      seller_group_id: groupIds,
      seller_module_id: sellerModule.id,
    },
  });

  if (!sellerGroupPermission) return false;

  const permissionKey = `${action}_permission`;

  return Boolean(sellerGroupPermission[permissionKey]);
}

async function isSellerHasFeatureFlag(req) {
  try {
    const { seller_id } = req;

    const featureFlag = await SellerFeatureFlags.findOne({
      where: {
        name: SELLER_USER_PERMISSIONS_FLAG,
        status: true,
      },
    });

    if (!featureFlag) {
      return false;
    }

    const sellerFeatureControlFlag = await SellerControlFeatureFlags.findOne({
      where: {
        feature_flag_id: featureFlag.id,
        seller_company_id: seller_id,
        enabled: true,
        status: true,
      },
    });

    return !!sellerFeatureControlFlag;
  } catch (err) {
    throw err;
  }
}

/**
 * Generates a unique cache key based on request parameters using CryptoJS.
 *
 * @param {string} prefix - Optional prefix for the key (e.g., "sellersCompany")
 * @param {object} params - All request query/filter parameters
 * @returns {string} - A unique string suitable as a Redis key
 */
function generateCacheKey(prefix = "cache", params = {}) {
  if (!params || typeof params !== "object") {
    params = {};
  }

  const sortedParams = {};
  Object.keys(params)
    .sort()
    .forEach((key) => {
      sortedParams[key] = params[key];
    });

  const paramString = JSON.stringify(sortedParams);

  const hash = crypto.MD5(paramString).toString();

  return `${prefix}:${hash}`;
}

const roundMoney = (value, decimals = 2) => {
  if (value === null || value === undefined) return 0;

  const num = Number(value);

  if (!Number.isFinite(num)) return 0;

  if (!Number.isInteger(decimals) || decimals < 0) decimals = 3;

  const factor = 10 ** decimals;

  return Math.round((num + Number.EPSILON) * factor) / factor;
};

module.exports = {
  generateRandomString,
  generateRandomNumber,
  removeSpecialCharactersAndSpaces,
  generateSecureRandomSixDigit,
  getCurrentDateTimeInGMT8,
  filterAndFormatPhones,
  formatDate12Hour,
  formatDateWithoutTime,
  isValidDate,
  formatEncodeDate,
  generateRandomWords,
  isBase64,
  filterUpdateValues,
  SELLER_USER_PERMISSIONS_FLAG,
  SELLER_SETTINGS_MODULE,
  generateReferenceNumber,
  isUserHasPermissionToDoAction,
  isSellerHasFeatureFlag,
  SELLER_ORDERS_MODULE,
  SELLER_VOUCHERS_MODULE,
  SELLER_PRODUCTS_MODULE,
  SELLER_EXPEDITE_ORDERS,
  SELLER_PURCHASE_ORDERS,
  generateCacheKey,
  roundMoney,
};
