import axios from "axios";
import moment from "moment";
import { PDFDocument, rgb, degrees } from "pdf-lib";
import Parse from "parse";
import { appInfo } from "./appinfo";
import { saveAs } from "file-saver";
import printModule from "print-js";
import fontkit from "@pdf-lib/fontkit";
import { themeColor } from "./const";
import { format, toZonedTime } from "date-fns-tz";
import i18n from "../i18n";
import { applyNumberFormulasToPages, buildDownloadFilename } from "../utils";

export const fontsizeArr = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28];
export const fontColorArr = ["red", "black", "blue", "yellow"];
export const isMobile = window.innerWidth < 767;
export const isTab = 767 < window.innerWidth < 1023;
export const isHighResolution = window.innerWidth > 1023;
export const isTabAndMobile = window.innerWidth < 1023;
export const textInputWidget = "text input";
export const textWidget = "text";
export const radioButtonWidget = "radio button";
export const cellsWidget = "cells";
export function getEnv() {
  return window?.RUNTIME_ENV || {};
}
const appName = "OpenSign™";

export const defaultMailBody = `<p>Hi {{receiver_name}},</p><br><p>We hope this email finds you well. {{sender_name}}&nbsp;has requested you to review and sign&nbsp;{{document_title}}.</p><p>Your signature is crucial to proceed with the next steps as it signifies your agreement and authorization.</p><br><p><a href='{{signing_url}}' rel='noopener noreferrer' target='_blank'>Sign here</a></p><br><br><p>If you have any questions or need further clarification regarding the document or the signing process,  please contact the sender.</p><br><p>Thanks</p><p> Team ${appName}</p><br>`;
export const defaultMailSubject = `{{sender_name}} has requested you to sign {{document_title}}`;

export const randomId = (digit = 8) => {
  // 1. Grab a cryptographically-secure 32-bit random value
  // Use crypto for stronger randomness
  const randomBytes = crypto.getRandomValues(new Uint32Array(1));
  const raw = randomBytes[0]; // 0 … 4,294,967,295

  // Calculate the min and max for the given digit length
  const min = Math.pow(10, digit - 1); // e.g., digit=3 → 100
  const max = Math.pow(10, digit) - 1; // e.g., digit=3 → 999
  const range = max - min + 1;

  // Collapse random value into the range and shift
  return min + (raw % range);
};
//function for create list of year for date widget
export const range = (start, end, step) => {
  const range = [];
  for (let i = start; i <= end; i += step) {
    range.push(i);
  }
  return range;
};
//function for get year
export const getYear = (date) => {
  const newYear = new Date(date).getFullYear();
  return newYear;
};
export const years = range(1950, getYear(new Date()) + 16, 1);
export const months = [
  "January",
  "February",
  "March",
  "April",
  "May",
  "June",
  "July",
  "August",
  "September",
  "October",
  "November",
  "December"
];
export const fileasbytes = async (filepath) => {
  const response = await fetch(filepath); // Adjust the path accordingly
  const arrayBuffer = await response.arrayBuffer();
  return new Uint8Array(arrayBuffer);
};

export const openInNewTab = (url, target) => {
  if (target) {
    window.open(url, target, "noopener,noreferrer");
  } else {
    window.open(url, "_blank", "noopener,noreferrer");
  }
};

export const getUserCountry = async () => {
  try {
    const res = await fetch("https://ipapi.co/json/");
    const data = await res.json();
    return data?.country_code;
  } catch (err) {
    console.log("Error fetching country", err);
    return "";
  }
};

// `getSecureUrl` is used to return local secure url if local files
export const getSecureUrl = async (url) => {
  const fileUrl = new URL(url)?.pathname?.includes("files");
  if (fileUrl) {
    try {
      const fileRes = await Parse.Cloud.run("fileupload", { url: url });
      if (fileRes.url) {
        return { url: fileRes.url };
      } else {
        return { url: "" };
      }
    } catch (err) {
      console.log("err while fileupload ", err);
      return { url: "" };
    }
  } else {
    return { url: url };
  }
};

/**
 * Removes a trailing path segment from a URL string, if present.
 *
 * @param {string} url     - The original URL.
 * @param {string} segment - The segment to strip off (default: "app").
 * @returns {string}       - The URL with the trailing segment removed, or unmodified if it didn’t match.
 */
export function removeTrailingSegment(url, segment = "app") {
  // Normalize a trailing slash (e.g. “/app/” → “/app”)
  const normalized = url.endsWith("/") ? url.slice(0, -1) : url;

  const lastSlash = normalized.lastIndexOf("/");
  const lastPart = normalized.slice(lastSlash + 1);

  if (lastPart === segment) {
    return normalized.slice(0, lastSlash);
  }

  return normalized;
}

export const color = [
  "#93a3db",
  "#e6c3db",
  "#c0e3bc",
  "#bce3db",
  "#b8ccdb",
  "#ceb8db",
  "#ffccff",
  "#99ffcc",
  "#cc99ff",
  "#ffcc99",
  "#66ccff",
  "#ffffcc"
];

export const nameColor = [
  "#304fbf",
  "#7d5270",
  "#5f825b",
  "#578077",
  "#576e80",
  "#6d527d",
  "#cc00cc",
  "#006666",
  "#cc00ff",
  "#ff9900",
  "#336699",
  "#cc9900"
];
export const toDataUrl = (file) => {
  return new Promise((resolve) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onloadend = (e) => {
      resolve(e.target.result);
    };
  });
};

//function for getting document details for getDrive cloud function
export const getDrive = async (documentId, skip = 0, limit = 50) => {
  const data = {
    docId: documentId && documentId,
    limit: limit,
    skip: skip
  };
  const driveDeatils = await axios
    .post(`${localStorage.getItem("baseUrl")}functions/getDrive`, data, {
      headers: {
        "Content-Type": "application/json",
        "X-Parse-Application-Id": localStorage.getItem("parseAppId"),
        sessiontoken: localStorage.getItem("accesstoken")
      }
    })
    .then((Listdata) => {
      const json = Listdata.data;

      if (json && json.result.error) {
        return json;
      } else if (json && json.result) {
        const data = json.result;
        return data;
      } else {
        return [];
      }
    })
    .catch((err) => {
      console.log("Err in getDrive cloud function", err);
      return "Error: Something went wrong!";
    });

  return driveDeatils;
};

// `pdfNewWidthFun` function is used to calculate pdf width to render in middle container
export const pdfNewWidthFun = (divRef) => {
  const pdfWidth = divRef.current.offsetWidth;
  return pdfWidth;
};

//`contractUsers` function is used to get contract_User details
export const contractUsers = async () => {
  try {
    const url = `${localStorage.getItem("baseUrl")}functions/getUserDetails`;
    const parseAppId = localStorage.getItem("parseAppId");
    const accesstoken = localStorage.getItem("accesstoken");
    const token = { "X-Parse-Session-Token": accesstoken };
    const headers = {
      headers: {
        "Content-Type": "application/json",
        "X-Parse-Application-Id": parseAppId,
        ...token
      }
    };
    const userDetails = await axios.post(url, {}, headers);
    let data = [];
    if (userDetails?.data?.result) {
      const json = JSON.parse(JSON.stringify(userDetails.data.result));
      data.push(json);
    }
    return data;
  } catch (err) {
    console.log("Err in getUserDetails cloud function", err);
    return "Error: Something went wrong!";
  }
};

//function for resize image and update width and height for mulitisigners
export const handleImageResize = (
  ref,
  key,
  signerPos,
  setSignerPos,
  pageNumber,
  containerScale,
  scale,
  signerId,
  showResize
) => {
  // Compute widget dimensions only once
  const { offsetWidth, offsetHeight } = ref;
  const factor = scale * containerScale || 1;
  const widgetWidth = offsetWidth / factor;
  const widgetHeight = offsetHeight / factor;
  const widgetDims = { Width: widgetWidth, Height: widgetHeight };

  const filterSignerPos = signerPos.filter((data) => data.Id === signerId);
  if (filterSignerPos.length > 0) {
    const getPlaceHolder = filterSignerPos[0].placeHolder;
    const getPageNumer = getPlaceHolder.filter(
      (data) => data.pageNumber === pageNumber
    );
    if (getPageNumer.length > 0) {
      const getXYdata = getPageNumer[0].pos;
      const getPosData = getXYdata;
      const addSignPos = getPosData.map((url) => {
        if (url.key === key) {
          // Base fields for every resized signature
          const base = {
            ...url,
            Width: widgetWidth,
            Height: widgetHeight,
            IsResize: showResize ? true : false
          };
          // If it's a “type” signature, regenerate the image and options
          if (url.typeSignature && url.signatureType === "type") {
            const signImg = convertTextToImg(
              url.typeFont ?? "Fasthand",
              url.typeSignature,
              url.fontColor ?? "blue",
              widgetDims
            );
            return {
              ...base,
              SignUrl: signImg,
              options: { ...url.options, response: signImg }
            };
          }
          return base;
        }
        return url;
      });

      const newUpdateSignPos = getPlaceHolder.map((obj) => {
        if (obj.pageNumber === pageNumber) {
          return { ...obj, pos: addSignPos };
        }
        return obj;
      });

      const newUpdateSigner = signerPos.map((obj) => {
        if (obj.Id === signerId) {
          return { ...obj, placeHolder: newUpdateSignPos };
        }
        return obj;
      });

      setSignerPos(newUpdateSigner);
    }
  }
};

export const widgets = [
  { type: "signature", icon: "fa-light fa-pen-nib", iconSize: "20px" },
  { type: "stamp", icon: "fa-light fa-stamp", iconSize: "19px" },
  { type: "initials", icon: "fa-light fa-signature", iconSize: "15px" },
  { type: textInputWidget, icon: "fa-light fa-font", iconSize: "21px" },
  { type: "name", icon: "fa-light fa-user", iconSize: "21px" },
  { type: "job title", icon: "fa-light fa-address-card", iconSize: "17px" },
  { type: "company", icon: "fa-light fa-building", iconSize: "25px" },
  { type: "email", icon: "fa-light fa-envelope", iconSize: "20px" },
  { type: "date", icon: "fa-light fa-calendar-days", iconSize: "20px" },
  { type: textWidget, icon: "fa-light fa-text-width", iconSize: "20px" },
  { type: cellsWidget, icon: "fa-light fa-table-cells", iconSize: "20px" },
  { type: "checkbox", icon: "fa-light fa-square-check", iconSize: "22px" },
  {
    type: "dropdown",
    icon: "fa-light fa-circle-chevron-down",
    iconSize: "19px"
  },
  { type: radioButtonWidget, icon: "fa-light fa-circle-dot", iconSize: "20px" },
  { type: "image", icon: "fa-light fa-image", iconSize: "20px" }
];

export const getDate = (dateformat) => {
  const format = dateformat || "MM/DD/YYYY";
  const date = new Date();
  const milliseconds = date.getTime();
  const newDate = moment(milliseconds).format(format);
  return newDate;
};

export const selectFormat = (data) => {
  switch (data) {
    case "L":
      return "MM/dd/yyyy";
    case "MM/DD/YYYY":
      return "MM/dd/yyyy";
    case "DD-MM-YYYY":
      return "dd-MM-yyyy";
    case "DD/MM/YYYY":
      return "dd/MM/yyyy";
    case "LL":
      return "MMMM dd, yyyy";
    case "DD MMM, YYYY":
      return "dd MMM, yyyy";
    case "YYYY-MM-DD":
      return "yyyy-MM-dd";
    case "MM-DD-YYYY":
      return "MM-dd-yyyy";
    case "MM.DD.YYYY":
      return "MM.dd.yyyy";
    case "MMM DD, YYYY":
      return "MMM dd, yyyy";
    case "MMMM DD, YYYY":
      return "MMMM dd, yyyy";
    case "DD MMMM, YYYY":
      return "dd MMMM, yyyy";
    case "DD.MM.YYYY":
      return "dd.MM.yyyy";
    default:
      return "MM/dd/yyyy";
  }
};

export const changeDateToMomentFormat = (format) => {
  switch (format) {
    case "MM/dd/yyyy":
      return "L";
    case "dd-MM-yyyy":
      return "DD-MM-YYYY";
    case "dd/MM/yyyy":
      return "DD/MM/YYYY";
    case "MMMM dd, yyyy":
      return "LL";
    case "dd MMM, yyyy":
      return "DD MMM, YYYY";
    case "yyyy-MM-dd":
      return "YYYY-MM-DD";
    case "MM-dd-yyyy":
      return "MM-DD-YYYY";
    case "MM.dd.yyyy":
      return "MM.DD.YYYY";
    case "MMM dd, yyyy":
      return "MMM DD, YYYY";
    case "dd MMMM, yyyy":
      return "DD MMMM, YYYY";
    case "dd.MM.yyyy":
      return "DD.MM.YYYY";
    default:
      return "L";
  }
};

export const getSignerPages = (xyPosition = [], currentKey, signerId) => {
  if (!Array.isArray(xyPosition)) {
    return [];
  }
  const hasSigners = xyPosition.some((item) =>
    Array.isArray(item?.placeHolder)
  );
  if (hasSigners) {
    const candidates =
      signerId !== undefined && signerId !== null && signerId !== ""
        ? xyPosition.filter(
            (item) => item.Id === signerId || item.signerObjId === signerId
          )
        : xyPosition;
    for (const signer of candidates) {
      const pages = signer?.placeHolder || [];
      const ownsWidget = pages.some((page) =>
        (page?.pos || []).some((widget) => widget?.key === currentKey)
      );
      if (ownsWidget) {
        return pages;
      }
    }
    return [];
  }
  return xyPosition;
};

export const addWidgetOptions = (
  type,
  signer,
  placeholder,
  role,
  widgetValue
) => {
  let defaultOpt;
  if (placeholder) {
    const countSameWidget = placeholder?.reduce((count, page) => {
      return count + page.pos.filter((item) => item.type === type).length;
    }, 0);
    const count = countSameWidget + 1;
    defaultOpt = { name: `${type}-${count}`, status: "required" };
  } else {
    defaultOpt = { name: `${type}-1`, status: "required" };
  }
  switch (type) {
    case "signature":
      return defaultOpt;
    case "stamp":
      return defaultOpt;
    case "checkbox":
      return { ...defaultOpt, isReadOnly: false, isHideLabel: false };
    case textInputWidget:
      return { ...defaultOpt, isReadOnly: false };
    case cellsWidget:
      return {
        ...defaultOpt,
        cellCount: 5,
        defaultValue: "",
        validation: { type: "", pattern: "" },
        isReadOnly: false
      };
    case "initials":
      return defaultOpt;
    case "name":
      return { ...defaultOpt, defaultValue: widgetValue ? widgetValue : "" };
    case "company":
      return { ...defaultOpt, defaultValue: widgetValue ? widgetValue : "" };
    case "job title":
      return { ...defaultOpt, defaultValue: widgetValue ? widgetValue : "" };
    case "date": {
      const dateFormat = signer?.DateFormat
        ? selectFormat(signer?.DateFormat)
        : "MM/dd/yyyy";
      return {
        ...defaultOpt,
        response: role === "prefill" ? getDate(signer?.DateFormat) : "",
        validation: { format: dateFormat, type: "date-format" }
      };
    }
    case "image":
      return defaultOpt;
    case "email":
      return {
        ...defaultOpt,
        validation: { type: "email", pattern: "" },
        defaultValue: widgetValue ? widgetValue : ""
      };
    case "dropdown":
      return defaultOpt;
    case radioButtonWidget:
      return {
        ...defaultOpt,
        values: [],
        isReadOnly: false,
        isHideLabel: false
      };
    case textWidget:
      return defaultOpt;
    default:
      return {};
  }
};

export const addWidgetSelfsignOptions = (
  type,
  getWidgetValue,
  owner,
  placeholder
) => {
  let defaultOpt;
  //condition to handle widgets name field
  if (placeholder) {
    const countSameWidget = placeholder?.reduce((count, page) => {
      return count + page.pos.filter((item) => item.type === type).length;
    }, 0);
    const count = countSameWidget + 1;
    defaultOpt = { name: `${type}-${count}`, status: "required" };
  } else {
    defaultOpt = { name: `${type}-1`, status: "required" };
  }
  switch (type) {
    case "signature":
      return defaultOpt;
    case "stamp":
      return defaultOpt;
    case "checkbox":
      return defaultOpt;
    case textWidget:
      return defaultOpt;
    case cellsWidget:
      return {
        ...defaultOpt,
        cellCount: 5,
        defaultValue: "",
        validation: { type: "", pattern: "" },
        isReadOnly: false
      };
    case "initials":
      return defaultOpt;
    case "name":
      return {
        ...defaultOpt,
        defaultValue: getWidgetValue(type),
        validation: { type: "text", pattern: "" }
      };
    case "company":
      return {
        ...defaultOpt,
        defaultValue: getWidgetValue(type),
        validation: { type: "text", pattern: "" }
      };
    case "job title":
      return {
        ...defaultOpt,
        defaultValue: getWidgetValue(type),
        validation: { type: "text", pattern: "" }
      };
    case "date": {
      const dateFormat = owner?.DateFormat
        ? selectFormat(owner?.DateFormat)
        : "MM/dd/yyyy";
      return {
        ...defaultOpt,
        response: getDate(owner?.DateFormat),
        validation: { format: dateFormat, type: "date-format" }
      };
    }
    case "image":
      return defaultOpt;
    case "email":
      return {
        ...defaultOpt,
        defaultValue: getWidgetValue(type),
        validation: { type: "email", pattern: "" }
      };
    default:
      return {};
  }
};

export const defaultWidthHeight = (type) => {
  switch (type) {
    case "signature":
      return { width: 150, height: 60 };
    case "stamp":
      return { width: 150, height: 60 };
    case "checkbox":
      return { width: 15, height: 19 };
    case textInputWidget:
      return { width: 150, height: 19 };
    case cellsWidget:
      return { width: 112, height: 22 };
    case "dropdown":
      return { width: 120, height: 22 };
    case "initials":
      return { width: 50, height: 50 };
    case "name":
      return { width: 150, height: 19 };
    case "company":
      return { width: 150, height: 19 };
    case "job title":
      return { width: 150, height: 19 };
    case "date":
      return { width: 100, height: 20 };
    case "image":
      return { width: 70, height: 70 };
    case "email":
      return { width: 150, height: 19 };
    case radioButtonWidget:
      return { width: 5, height: 10 };
    case textWidget:
      return { width: 150, height: 19 };
    default:
      return { width: 150, height: 60 };
  }
};
export async function getBase64FromUrl(url, autosign) {
  const data = await fetch(url);
  const blob = await data.blob();
  return new Promise((resolve) => {
    const reader = new FileReader();
    reader.readAsDataURL(blob);
    reader.onloadend = function () {
      const pdfBase = this.result;
      if (autosign) {
        resolve(pdfBase);
      } else {
        const suffixbase64 = pdfBase.split(",").pop();
        resolve(suffixbase64);
      }
    };
  });
}

export async function getBase64FromIMG(url) {
  const data = await fetch(url);
  const blob = await data.blob();
  return new Promise((resolve) => {
    const reader = new FileReader();
    reader.readAsDataURL(blob);
    reader.onloadend = function () {
      const pdfBase = this.result;
      resolve(pdfBase);
    };
  });
}
//function for convert signature png base64 url to jpeg base64
export const convertPNGtoJPEG = (base64Data) => {
  return new Promise((resolve, reject) => {
    const canvas = document.createElement("canvas");
    const img = new Image();
    img.src = base64Data;

    img.onload = () => {
      canvas.width = img.width;
      canvas.height = img.height;

      const ctx = canvas.getContext("2d");
      ctx.fillStyle = "#ffffff"; // white color
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      ctx.drawImage(img, 0, 0);
      // Convert to JPEG by using the canvas.toDataURL() method
      const jpegBase64Data = canvas.toDataURL("image/jpeg");

      resolve(jpegBase64Data);
    };

    img.onerror = (error) => {
      reject(error);
    };
  });
};

//function for resize image and update width and height for sign-yourself
export const handleSignYourselfImageResize = (
  ref,
  key,
  xyPosition,
  setXyPosition,
  index,
  containerScale,
  scale
) => {
  // Guard against bad index
  if (!xyPosition[index]) {
    console.log(`ImageResize invalid index ${index}`);
    return;
  }

  // Compute widget dimensions only once
  const { offsetWidth, offsetHeight } = ref;
  const factor = scale * containerScale || 1;
  const widgetWidth = offsetWidth / factor;
  const widgetHeight = offsetHeight / factor;
  const widgetDims = { Width: widgetWidth, Height: widgetHeight };

  // Single pass to update only the targeted index/key
  const updated = xyPosition.map((item, idx) => {
    if (idx !== index) return item;

    return {
      ...item,
      pos: item.pos.map((url) => {
        if (url.key !== key) return url;

        // Base fields for every resized signature
        const base = {
          ...url,
          Width: widgetWidth,
          Height: widgetHeight,
          IsResize: true
        };

        // If it's a “type” signature, regenerate the image and options
        if (url.typeSignature && url.signatureType === "type") {
          const signImg = convertTextToImg(
            url.typeFont ?? "Fasthand",
            url.typeSignature,
            url.fontColor ?? "blue",
            widgetDims
          );

          return {
            ...base,
            SignUrl: signImg,
            options: { ...url.options, response: signImg }
          };
        }

        return base;
      })
    };
  });
  setXyPosition(updated);
};

//function for call cloud function signPdf and generate digital signature
export const signPdfFun = async (
  base64Url,
  documentId,
  signerObjectId,
  objectId,
  widgets
) => {
  let isCustomCompletionMail = false;
  try {
    //get tenant details
    const tenantDetails = await getTenantDetails(objectId);
    if (tenantDetails && tenantDetails === "user does not exist!") {
      return { status: "error", message: "User does not exist." };
    } else {
      if (tenantDetails?.CompletionBody && tenantDetails?.CompletionSubject) {
        isCustomCompletionMail = true;
      }
    }
    // below for loop is used to get first signature of user to send if to signpdf
    // for adding it in completion certificate
    let getSignature;
    for (let item of widgets) {
      if (!getSignature) {
        const typeExist = item.pos.some((data) => data?.type);
        if (typeExist) {
          getSignature = item.pos.find((data) => data?.type === "signature");
        } else {
          getSignature = item.pos.find((data) => !data.isStamp);
        }
      }
    }

    let base64Sign = getSignature?.SignUrl;
    //check https type signature (default signature exist) then convert in base64
    const isUrl = base64Sign.includes("https");
    if (isUrl) {
      try {
        base64Sign = await fetchImageBase64(base64Sign);
      } catch (e) {
        console.log("error", e);
        return { status: "error", message: "something went wrong." };
      }
    }
    //change image width and height to 300/120 in png base64
    const imagebase64 = await changeImageWH(base64Sign);
    //remove suffiix of base64 (without type)
    const suffixbase64 = imagebase64 && imagebase64.split(",").pop();

    const params = {
      pdfFile: base64Url,
      docId: documentId,
      userId: signerObjectId,
      isCustomCompletionMail: isCustomCompletionMail,
      signature: suffixbase64
    };
    const resSignPdf = await Parse.Cloud.run("signPdf", params);
    if (resSignPdf) {
      const signedPdf = JSON.parse(JSON.stringify(resSignPdf));
      return signedPdf;
    }
  } catch (e) {
    console.log("Err in signPdf cloud function ", e.message);
    if (e && e?.message?.includes("is encrypted.")) {
      return {
        status: "error",
        message: "Currently encrypted pdf files are not supported."
      };
    } else if (e?.message?.includes("password")) {
      return { status: "error", message: "PFX file password is invalid." };
    } else {
      return { status: "error", message: "something went wrong." };
    }
  }
};
export const createDocument = async (
  template,
  placeholders,
  signerData,
  SignedUrl,
  isSendDoc
) => {
  if (template && template.length > 0) {
    const Doc = template[0];
    const date = new Date();
    const isoDate = date.toISOString();
    let extUserId = Doc.ExtUserPtr.objectId;
    let creatorId = Doc.CreatedBy.objectId;
    const Extand_Class = localStorage.getItem("Extand_Class");
    const extClass = Extand_Class && JSON.parse(Extand_Class);
    if (extClass && extClass.length > 0) {
      if (Doc.ExtUserPtr?.objectId !== extClass?.[0]?.objectId) {
        if (extClass && extClass.length > 0) {
          extUserId = extClass[0].objectId;
          creatorId = extClass[0]?.UserId.objectId;
        }
      }
    }
    let placeholdersArr = [];
    if (placeholders?.length > 0) {
      placeholdersArr = placeholders;
    }
    let signers = [];
    if (signerData?.length > 0) {
      signerData.forEach((x) => {
        if (x.objectId) {
          const obj = {
            __type: "Pointer",
            className: "contracts_Contactbook",
            objectId: x.objectId
          };
          signers.push(obj);
        }
      });
    }
    const SignatureType = Doc?.SignatureType
      ? { SignatureType: Doc?.SignatureType }
      : {};
    const NotifyOnSignatures =
      Doc?.NotifyOnSignatures !== undefined
        ? { NotifyOnSignatures: Doc?.NotifyOnSignatures }
        : {};
    const Bcc = Doc?.Bcc?.length > 0 ? { Bcc: Doc?.Bcc } : {};
    const RedirectUrl = Doc?.RedirectUrl
      ? { RedirectUrl: Doc?.RedirectUrl }
      : {};
    const TemplateId = Doc?.objectId
      ? {
          TemplateId: {
            __type: "Pointer",
            className: "contracts_Template",
            objectId: Doc?.objectId
          }
        }
      : {};
    const data = {
      Name: Doc.Name,
      URL: SignedUrl ? SignedUrl : Doc?.URL,
      SignedUrl: isSendDoc ? SignedUrl : Doc.SignedUrl,
      SentToOthers: Doc.SentToOthers,
      Description: Doc.Description,
      Note: Doc.Note,
      Placeholders: placeholdersArr,
      ExtUserPtr: {
        __type: "Pointer",
        className: "contracts_Users",
        objectId: extUserId
      },
      CreatedBy: {
        __type: "Pointer",
        className: "_User",
        objectId: creatorId
      },
      Signers: signers,
      SendinOrder: Doc?.SendinOrder || false,
      AutomaticReminders: Doc?.AutomaticReminders || false,
      RemindOnceInEvery: parseInt(Doc?.RemindOnceInEvery || 5),
      IsEnableOTP: Doc?.IsEnableOTP || false,
      IsTourEnabled: Doc?.IsTourEnabled || false,
      AllowModifications: Doc?.AllowModifications || false,
      TimeToCompleteDays: parseInt(Doc?.TimeToCompleteDays) || 15,
      DocSentAt: { __type: "Date", iso: isoDate },
      ...SignatureType,
      ...NotifyOnSignatures,
      ...Bcc,
      ...RedirectUrl,
      ...TemplateId
    };
    const remindOnceInEvery = Doc?.RemindOnceInEvery;
    const TimeToCompleteDays = parseInt(Doc?.TimeToCompleteDays);
    const reminderCount = TimeToCompleteDays / remindOnceInEvery;
    const AutomaticReminders = Doc.autoreminder;
    if (AutomaticReminders && reminderCount > 15) {
      return { status: "error", id: "only-15-reminder-allowed" };
    }
    try {
      const res = await axios.post(
        `${localStorage.getItem("baseUrl")}classes/contracts_Document`,
        data,
        {
          headers: {
            "Content-Type": "application/json",
            "X-Parse-Application-Id": localStorage.getItem("parseAppId"),
            "X-Parse-Session-Token": localStorage.getItem("accesstoken")
          }
        }
      );
      if (res) {
        return { status: "success", id: res.data.objectId, data: res.data };
      }
    } catch (err) {
      console.log("axois err ", err);
      return { status: "error", id: "something-went-wrong-mssg" };
    }
  }
};

export const getFirstLetter = (name) => {
  const firstLetter = name?.charAt(0);
  return firstLetter;
};

export const darkenColor = (color, factor) => {
  // Remove '#' from the color code and parse it to get RGB values
  const hex = color.replace("#", "");
  const r = parseInt(hex.substring(0, 2), 16);
  const g = parseInt(hex.substring(2, 4), 16);
  const b = parseInt(hex.substring(4, 6), 16);

  // Darken the color by reducing each RGB component
  const darkerR = Math.floor(r * (1 - factor));
  const darkerG = Math.floor(g * (1 - factor));
  const darkerB = Math.floor(b * (1 - factor));

  // Convert the darkened RGB components back to hex
  return `#${((darkerR << 16) | (darkerG << 8) | darkerB)
    .toString(16)
    .padStart(6, "0")}`;
};

export const addZIndex = (signerPos, key, setZIndex) => {
  return signerPos.map((item) => {
    if (item.placeHolder && item.placeHolder.length > 0) {
      // If there is a nested array, recursively add the field to the last object
      return {
        ...item,
        placeHolder: addZIndex(item.placeHolder, key, setZIndex)
      };
    } else if (item.pos && item.pos.length > 0) {
      // If there is no nested array, add the new field
      return {
        ...item,
        pos: addZIndex(item.pos, key, setZIndex)
        // Adjust this line to add the desired field
      };
    } else {
      if (item.key === key) {
        setZIndex(item.zIndex);
        return {
          ...item,
          zIndex: item.zIndex ? item.zIndex + 1 : 1
        };
      } else {
        return {
          ...item
        };
      }
    }
  });
};

//function for save widgets value on onchange function
export const onChangeInput = (
  value,
  currentPosition,
  xyPosition,
  index,
  setXyPosition,
  userId,
  initial,
  dateFormat,
  fontSize,
  fontColor,
  dateDetails
) => {
  const isSigners = xyPosition.some(
    (data) => data.signerPtr || data.Role === "prefill"
  );
  let filterSignerPos;
  if (isSigners) {
    if (userId) {
      filterSignerPos = xyPosition.filter((data) => data.Id === userId);
    }
    const getPlaceHolder = filterSignerPos[0]?.placeHolder;
    if (initial) {
      const xyData = addInitialData(xyPosition, setXyPosition, value, userId);
      setXyPosition(xyData);
    } else {
      const getPageNumer = getPlaceHolder.filter(
        (data) => data.pageNumber === index
      );
      if (getPageNumer.length > 0) {
        const getXYdata = getPageNumer[0].pos;
        const addSignPos = getXYdata.map((position) => {
          if (
            (position?.options?.name === currentPosition?.options?.name &&
              filterSignerPos[0].Role === "prefill") ||
            position.key === currentPosition.key
          ) {
            if (dateFormat) {
              return {
                ...position,
                options: {
                  ...position.options,
                  response: value,
                  fontSize: fontSize ? fontSize : position.options?.fontSize,
                  fontColor: fontColor
                    ? fontColor
                    : position.options?.fontColor,
                  isReadOnly:
                    dateDetails?.isReadOnly !== "undefined"
                      ? dateDetails?.isReadOnly
                      : position.options?.isReadOnly,
                  status:
                    dateDetails?.status !== "undefined"
                      ? dateDetails?.status
                      : position.options?.status,
                  validation: {
                    type: "date-format",
                    format: dateFormat // This indicates the required date format explicitly.
                  }
                }
              };
            } else {
              return {
                ...position,
                options: {
                  ...position.options,
                  response: value,
                  defaultValue: ""
                }
              };
            }
          }
          return position;
        });
        const newUpdateSignPos = getPlaceHolder.map((obj) => {
          if (obj.pageNumber === index) {
            return { ...obj, pos: addSignPos };
          }
          return obj;
        });

        const recalculatedPages = applyNumberFormulasToPages(newUpdateSignPos);

        const newUpdateSigner = xyPosition.map((obj) => {
          if (obj.Id === userId) {
            return { ...obj, placeHolder: recalculatedPages };
          }
          return obj;
        });

        setXyPosition(newUpdateSigner);
      }
    }
  } else {
    let getXYdata = xyPosition[index].pos;
    const updatePosition = getXYdata.map((positionData) => {
      if (positionData.key === currentPosition.key) {
        if (dateFormat) {
          return {
            ...positionData,
            options: {
              ...positionData.options,
              response: value,
              fontSize: fontSize,
              fontColor: fontColor,
              validation: {
                type: "date-format",
                format: dateFormat // This indicates the required date format explicitly.
              }
            }
          };
        } else {
          return {
            ...positionData,
            options: {
              ...positionData.options,
              response: value,
              defaultValue: ""
            }
          };
        }
      }
      return positionData;
    });

    const updatePlaceholder = xyPosition.map((obj, ind) => {
      if (ind === index) {
        return { ...obj, pos: updatePosition };
      }
      return obj;
    });
    setXyPosition(applyNumberFormulasToPages(updatePlaceholder));
  }
};
//function to increase height of text area on press enter
export const onChangeHeightOfTextArea = (
  height,
  widgetType,
  signKey,
  xyPosition,
  index,
  setXyPosition,
  userId
) => {
  const isSigners = xyPosition.some((data) => data.signerPtr);
  let filterSignerPos;
  if (isSigners) {
    if (userId) {
      filterSignerPos = xyPosition.filter((data) => data.Id === userId);
    }
    const getPlaceHolder = filterSignerPos[0]?.placeHolder;

    const getPageNumer = getPlaceHolder.filter(
      (data) => data.pageNumber === index
    );
    if (getPageNumer.length > 0) {
      const getXYdata = getPageNumer[0].pos;
      const getPosData = getXYdata;
      const addSignPos = getPosData.map((position) => {
        if (position.key === signKey) {
          return {
            ...position,
            Height: position.Height
              ? position.Height + height
              : defaultWidthHeight(widgetType).height + height
          };
        }
        return position;
      });
      const newUpdateSignPos = getPlaceHolder.map((obj) => {
        if (obj.pageNumber === index) {
          return { ...obj, pos: addSignPos };
        }
        return obj;
      });

      const newUpdateSigner = xyPosition.map((obj) => {
        if (obj.Id === userId) {
          return { ...obj, placeHolder: newUpdateSignPos };
        }
        return obj;
      });

      setXyPosition(newUpdateSigner);
    }
  } else {
    let getXYdata = xyPosition[index].pos;

    const updatePosition = getXYdata.map((position) => {
      if (position.key === signKey) {
        return {
          ...position,
          Height: position.Height
            ? position.Height + height
            : defaultWidthHeight(widgetType).height + height
        };
      }
      return position;
    });

    const updatePlaceholder = xyPosition.map((obj, ind) => {
      if (ind === index) {
        return { ...obj, pos: updatePosition };
      }
      return obj;
    });
    setXyPosition(updatePlaceholder);
  }
};
//calculate width and height
export const calculateInitialWidthHeight = (widgetData) => {
  const intialText = widgetData;
  const span = document.createElement("span");
  span.textContent = intialText;
  span.style.font = `12px`; // here put your text size and font family
  span.style.display = "hidden";
  document.body.appendChild(span);
  const width = span.offsetWidth;
  const height = span.offsetHeight;

  document.body.removeChild(span);
  return { getWidth: width, getHeight: height };
};
export const widgetDataValue = (type, value) => {
  switch (type) {
    case "name":
      return value?.Name || value?.name;
    case "company":
      return value?.Company || value?.company;
    case "job title":
      return value?.JobTitle || value?.jobTitle;
    case "email":
      return value?.Email || value?.email;
    default:
      return "";
  }
};
export const addInitialData = (signerPos, setXyPosition, value, userId) => {
  return signerPos.map((item) => {
    if (item.placeHolder && item.placeHolder.length > 0) {
      // If there is a nested array, recursively add the field to the last object
      if (item.Id === userId) {
        return {
          ...item,
          placeHolder: addInitialData(
            item.placeHolder,
            setXyPosition,
            value,
            userId
          )
        };
      } else {
        return item;
      }
    } else if (item.pos && item.pos.length > 0) {
      // If there is no nested array, add the new field
      return {
        ...item,
        pos: addInitialData(item.pos, setXyPosition, value, userId)
        // Adjust this line to add the desired field
      };
    } else {
      const widgetData = widgetDataValue(item.type, value);
      if (["name", "company", "job title", "email"].includes(item.type)) {
        return {
          ...item,
          options: {
            ...item.options,
            defaultValue: item?.options?.defaultValue || widgetData
          }
        };
      } else {
        return item;
      }
    }
  });
};

//function for embed document id
export const embedDocId = async (pdfOriginalWH, pdfDoc, documentId) => {
  const appName = "OpenSign™";
  // `fontBytes` is used to embed custom font in pdf
  const fontBytes = await fileasbytes(
    "https://cdn.opensignlabs.com/webfonts/times.ttf"
  );
  pdfDoc.registerFontkit(fontkit);
  const font = await pdfDoc.embedFont(fontBytes, { subset: true });
  //pdfOriginalWH contained all pdf's pages width and height
  for (let i = 0; i < pdfOriginalWH?.length; i++) {
    const fontSize = 10;
    const textContent = documentId && `${appName} DocumentId: ${documentId} `;
    const pages = pdfDoc.getPages();
    const page = pages[i];
    const getSize = pdfOriginalWH[i];
    try {
      const getObj = compensateRotation(
        page.getRotation().angle,
        10,
        5,
        1,
        getSize,
        fontSize,
        rgb(0.5, 0.5, 0.5),
        font,
        page
      );
      page.drawText(textContent, getObj);
    } catch (err) {
      console.log("Err in embed docId on page", i + 1, err?.message);
    }
  }
};

// function for convert input text value in image
export function convertTextToImg(fontStyle, text, color, widgetDims) {
  // read your widget dimensions:
  const maxWidth = widgetDims.Width;
  const maxHeight = widgetDims.Height;
  const baselineFontSizePx = maxHeight;
  const fontFamily = fontStyle || "Fasthand";
  const fillColor = color || "blue";

  // 1. Use a temporary canvas to measure actual text size
  const tempCanvas = document.createElement("canvas");
  const tempCtx = tempCanvas.getContext("2d");
  tempCtx.font = `${baselineFontSizePx}px ${fontFamily}`;
  const metrics = tempCtx.measureText(text);

  const actualHeight =
    metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
  const actualWidth = metrics.width;

  // const renderscale = 3;
  // 2. Scale font size so text height fills canvas
  const scaleX = maxWidth / actualWidth;
  const scaleY = maxHeight / actualHeight;
  const scale = Math.min(scaleX, scaleY, 1);
  // final font-size
  // const finalFontSizePx = baselineFontSizePx * scale * renderscale;
  const finalFontSizePx = baselineFontSizePx * scale;
  // 3. Create the final canvas
  const pxRatio = window.devicePixelRatio * 2 || 3;
  const canvas = document.createElement("canvas");
  canvas.width = Math.ceil(maxWidth * pxRatio);
  canvas.height = Math.ceil(maxHeight * pxRatio);
  const ctx = canvas.getContext("2d");
  ctx.scale(pxRatio, pxRatio);

  // 4. Setup text with final font
  ctx.font = `${finalFontSizePx}px ${fontFamily}`;
  ctx.fillStyle = fillColor;
  ctx.textAlign = "center";
  ctx.textBaseline = "alphabetic";

  const newMetrics = ctx.measureText(text);
  const ascent = newMetrics.actualBoundingBoxAscent;
  const descent = newMetrics.actualBoundingBoxDescent;

  // 5. Align text vertically so it touches top and bottom
  const y = maxHeight / 2 + (ascent - descent) / 2;

  ctx.clearRect(0, 0, maxWidth, maxHeight);
  ctx.fillText(text, maxWidth / 2, y);

  // 6. Output image
  const dataUrl = canvas.toDataURL("image/png");
  return dataUrl;
}

// `onSaveSign` trigger on save button to save for signature, initials widget for type: draw, typed, default
export function onSaveSign(
  type,
  xyPosition,
  index,
  signKey,
  signatureImg,
  updatedImgWH,
  isTypeText,
  typedSignature,
  isAutoSign,
  widgetsType,
  isApplyAll,
  typeFont,
  fontColor
) {
  let getIMGWH, posWidth, posHeight;
  let getXYdata = xyPosition[index].pos;
  const isSignOrInitials =
    widgetsType && ["signature", "initials"].includes(widgetsType);
  const updateXYData = getXYdata.map((position) => {
    if (position.key === signKey) {
      if (isTypeText) {
        getIMGWH = {
          newWidth: updatedImgWH.width,
          newHeight: updatedImgWH.height
        };
      }
      posWidth = getIMGWH ? getIMGWH.newWidth : 150;
      posHeight = getIMGWH ? getIMGWH.newHeight : 60;
      const widgetDims = { Width: posWidth, Height: posHeight };
      const signImg = typedSignature
        ? convertTextToImg(typeFont, typedSignature, fontColor, widgetDims)
        : signatureImg;
      return {
        ...position,
        ...(type === "type" ? { Width: posWidth } : {}),
        ...(type === "type" ? { Height: posHeight } : {}),
        SignUrl: signImg,
        ...(isSignOrInitials && { signatureType: type || "" }),
        options: { ...position.options, response: signImg },
        ...(typedSignature && {
          typeSignature: typedSignature,
          typeFont: typeFont ?? "Fasthand",
          fontColor: fontColor ?? "blue"
        })
      };
    }
    return position;
  });

  const updateXYposition = xyPosition.map((obj, ind) => {
    if (ind === index) {
      return { ...obj, pos: updateXYData };
    }
    return obj;
  });
  //condition  when draw/upload signature/initials then apply it all related to widgets (draw, typed signature or default signature)
  if (isApplyAll || isAutoSign) {
    const updatedArray = updateXYposition.map((page) => ({
      ...page,
      pos: page.pos.map((item) => {
        if (item.type === widgetsType) {
          const widgetDims = { Width: item.Width, Height: item.Height };
          const signImg = typedSignature
            ? convertTextToImg(typeFont, typedSignature, fontColor, widgetDims)
            : signatureImg;
          return {
            ...item,
            SignUrl: signImg,
            ...(isSignOrInitials && { signatureType: type || "" }),
            options: { ...item.options, response: signImg },
            ...(typedSignature && {
              typeSignature: typedSignature,
              typeFont: typeFont || "Fasthand",
              fontColor: fontColor || "blue"
            })
          };
        }
        return item; // Otherwise, keep it unchanged
      })
    }));
    return updatedArray;
  } else {
    return updateXYposition;
  }
}

/**
 * Scales and centers a base64‐encoded image into a fixed‐size widget
 * and returns a new base64 PNG.
 *
 * @param {string} base64Image  A data-URL (e.g. "data:image/png;base64,…")
 * @param {{ Width: number, Height: number }} widgetDims
 * @returns {Promise<string>}  A Promise that resolves to a data-URL of the new image
 */
export async function convertBase64ToImg(base64Image, widgetDims) {
  const { Width: maxWidth, Height: maxHeight } = widgetDims;

  // Detect input format from data URL
  const match = base64Image.match(/^data:(image\/(png|jpeg|jpg|webp));base64,/);
  const inputMime = match ? match[1] : "image/png"; // fallback to PNG if unknown

  // 1. Load the image off-DOM
  const img = new Image();
  img.src = base64Image;
  await new Promise((resolve, reject) => {
    img.onload = resolve;
    img.onerror = reject;
  });

  // 2. Compute scale to fit within widget (preserving aspect ratio)
  const { naturalWidth: imgW, naturalHeight: imgH } = img;
  const scale = Math.min(maxWidth / imgW, maxHeight / imgH, 1);
  const drawW = imgW * scale;
  const drawH = imgH * scale;

  // 3. Prepare a high-DPI canvas
  const pxRatio = (window.devicePixelRatio || 1) * 2;
  const canvas = document.createElement("canvas");
  canvas.width = Math.ceil(maxWidth * pxRatio);
  canvas.height = Math.ceil(maxHeight * pxRatio);
  const ctx = canvas.getContext("2d");
  ctx.scale(pxRatio, pxRatio);
  ctx.clearRect(0, 0, maxWidth, maxHeight);

  // 4. Center the image in the widget rectangle
  const x = (maxWidth - drawW) / 2;
  const y = (maxHeight - drawH) / 2;
  ctx.drawImage(img, x, y, drawW, drawH);
  // 5. Return new base64 in same format as input
  // const quality =
  //   inputMime.includes("jpeg") || inputMime.includes("jpg") ? 0.9 : undefined;
  // return canvas.toDataURL(inputMime, quality);
  // 5. Always return PNG (lossless, no quality param needed)
  return canvas.toDataURL("image/png");
}

//function to use After setting the signature URL for the first signature widget, clicking on subsequent
//signature widgets should automatically apply and display the signature. apply for initial,signature and stamp widget
export const handleCopySignUrl = (
  currentPos,
  existSignPosition,
  setXyPosition,
  xyPosition,
  pageNumber,
  signerObjectId
) => {
  //get current signer details
  const currentSigner = xyPosition.filter(
    (data) => data.signerObjId === signerObjectId
  );
  //get current signer placeholder details
  const placeholderPosition = currentSigner[0].placeHolder;
  //get current pagenumber position
  const getcurrentPagePosition = placeholderPosition.find(
    (data) => data.pageNumber === pageNumber
  );
  let getXYdata = getcurrentPagePosition.pos;
  const updatePos = getXYdata.map((x) => {
    //update widgets sign url details
    if (x.key === currentPos.key) {
      return {
        ...x,
        // Width: existSignPosition.Width,
        // Height: existSignPosition.Height,
        SignUrl: existSignPosition.SignUrl,
        signatureType: existSignPosition.signatureType,
        options: { ...x.options, response: existSignPosition.SignUrl },
        ...(existSignPosition.typedSignature && {
          typeSignature: existSignPosition.typedSignature
        })
      };
    }
    return x;
  });
  const updateXYposition = placeholderPosition.map((obj) => {
    if (obj.pageNumber === pageNumber) {
      return { ...obj, pos: updatePos };
    }
    return obj;
  });
  const newUpdateSigner = xyPosition.map((obj) => {
    if (obj.signerObjId === signerObjectId) {
      return { ...obj, placeHolder: updateXYposition };
    }
    return obj;
  });
  setXyPosition(newUpdateSigner);
};
export const calculateImgAspectRatio = (imgWH, pos) => {
  let newWidth, newHeight;

  const placeholderHeight = pos && pos.Height ? pos.Height : 60;
  const aspectRatio = imgWH.width / imgWH.height;
  newWidth = aspectRatio * placeholderHeight;
  newHeight = placeholderHeight;
  return { newHeight, newWidth };
};

// `onSaveImage` trigger for upload image through stamp, image, signature, initials widgets
export function onSaveImage(
  signatureType,
  xyPosition,
  index,
  signKey,
  image,
  isAutoSign,
  widgetsType,
  isApplyAll,
  imgUrl
) {
  // let getIMGWH;
  const isSignOrInitials =
    widgetsType && ["signature", "initials"].includes(widgetsType);
  //get current page position
  const getXYData = xyPosition[index].pos;
  const updateXYData = getXYData.map((position) => {
    if (position.key === signKey) {
      return {
        ...position,
        SignUrl: imgUrl || image.src,
        ImageType: image.imgType,
        ...(isSignOrInitials && { signatureType: signatureType || "" }),
        options: {
          ...position.options,
          response: imgUrl || image.src
        }
      };
    }
    return position;
  });
  const updateXYposition = xyPosition.map((obj, ind) => {
    if (ind === index) {
      return { ...obj, pos: updateXYData };
    }
    return obj;
  });
  // condition when user upload(stamp) then apply it all related to widgets
  if (isApplyAll || isAutoSign) {
    const updatedArray = updateXYposition.map((page) => ({
      ...page,
      pos: page.pos.map(
        (item) =>
          // below condition to exclude apply all for image widget
          item.type === widgetsType && item.type !== "image"
            ? {
                ...item,
                SignUrl: image.src,
                ImageType: image.imgType,
                options: {
                  ...item.options,
                  response: image.src
                }
              }
            : item // Otherwise, keep it unchanged
      )
    }));
    return updatedArray;
  } else {
    return updateXYposition;
  }
}

//function for select image and upload image
export const onImageSelect = (setImage, file) => {
  const reader = new FileReader();
  reader.readAsDataURL(file);
  reader.onloadend = function (e) {
    let width, height;
    const image = new Image();
    image.src = e.target.result;
    image.onload = function () {
      width = image.width;
      height = image.height;
      const aspectRatio = 460 / 184;
      const imgR = width / height;
      if (imgR > aspectRatio) {
        width = 460;
        height = 460 / imgR;
      } else {
        width = 184 * imgR;
        height = 184;
      }
    };
    image.src = reader.result;
    setImage({ src: image.src, imgType: file.type });
  };
};
export const compressedFileSize = (file, setImage) => {
  // Create a new FileReader instance to read the uploaded file
  const reader = new FileReader();

  // Event listener triggered when the file is read successfully
  reader.onload = (e) => {
    // Create a new Image instance to manipulate the image data
    const img = new Image();

    // Set the image source to the file's data URL (base64 string)
    img.src = e.target.result;

    // Event listener triggered when the image is fully loaded
    img.onload = () => {
      // Create a canvas element to resize and compress the image
      const canvas = document.createElement("canvas");
      const ctx = canvas.getContext("2d"); // Get the drawing context of the canvas

      // Set the canvas dimensions, ensuring they don't exceed 1920x1920
      canvas.width = Math.min(img.width, 1920); // Limit width to 1920px
      canvas.height = Math.min(img.height, 1920); // Limit height to 1920px

      // Draw the image onto the canvas with the specified dimensions
      ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
      // Get the original file type (default to "image/png" if not recognized)
      const fileType = "image/png"; //file.type || "image/png"
      // Compress the image and convert it into a Blob
      canvas.toBlob(
        (blob) => {
          // Create a new File object from the compressed Blob
          const compressedFile = new File([blob], file.name, {
            type: fileType, // Set the file type to JPEG
            lastModified: Date.now() // Update the last modified timestamp
          });
          // Pass the compressed file to a custom function for further processing
          onImageSelect(setImage, compressedFile);
        },
        fileType, // Output format for the compressed image
        0.3 // Compression quality (30%)
      );
    };
  };

  // Read the file as a data URL (base64 string) to be processed
  reader.readAsDataURL(file);
};
//convert https url to base64
export const fetchImageBase64 = async (imageUrl) => {
  try {
    const response = await fetch(imageUrl);
    const blob = await response.blob();

    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.readAsDataURL(blob);
      reader.onloadend = () => {
        const base64data = reader.result;
        resolve(base64data);
      };
      reader.onerror = (error) => {
        reject(error);
      };
    });
  } catch (error) {
    throw new Error("Error converting URL to base64:", error);
  }
};
//function for select image and upload image
export const changeImageWH = async (base64Image) => {
  const newWidth = 300;
  const newHeight = 120;
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.src = base64Image;
    img.onload = async () => {
      const canvas = document.createElement("canvas");
      const ctx = canvas.getContext("2d");
      canvas.width = newWidth;
      canvas.height = newHeight;
      ctx.drawImage(img, 0, 0, newWidth, newHeight);
      const resizedBase64 = canvas.toDataURL("image/png", 1.0);
      resolve(resizedBase64);
    };
    img.onerror = (error) => {
      reject(error);
    };
  });
};

const getWidgetsFontColor = (type) => {
  switch (type) {
    case "red":
      return rgb(1, 0, 0);
    case "black":
      return rgb(0, 0, 0);
    case "blue":
      return rgb(0, 0, 1);
    case "yellow":
      return rgb(0.9, 1, 0);
    default:
      return rgb(0, 0, 0);
  }
};
export const isBase64 = (str) => {
  const base64Pattern =
    /^data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+)?;base64,[A-Za-z0-9+/=]+$/;
  return base64Pattern.test(str);
};
//function for embed all type widgets in document using pdf-lib
export const embedWidgetsToDoc = async (
  widgets,
  pdfDoc,
  signyourself,
  scale,
  prefillImg
) => {
  // `fontBytes` is used to embed custom font in pdf
  const fontBytes = await fileasbytes(
    "https://cdn.opensignlabs.com/webfonts/times.ttf"
  );
  pdfDoc.registerFontkit(fontkit);
  const font = await pdfDoc.embedFont(fontBytes, { subset: true });
  let hasError = false;
  for (let item of widgets) {
    if (hasError) break; // Stop the outer loop if an error occurred
    const typeExist = item.pos.some((data) => data?.type);
    let updateItem;
    if (typeExist) {
      if (signyourself) {
        updateItem = item.pos;
      } else {
        // Checking required and optional widget types
        // For both required and optional widgets, handle signurl, defaultValue, and response as the widget's data
        // If the widget type is checkbox or radio (whether required or optional), we don't need to validate its value.
        // Instead, add an empty checkbox/radio, or if a value exists, mark the checkbox/radio as checked.
        updateItem = item.pos.filter(
          (data) =>
            data?.options?.SignUrl ||
            data?.options?.defaultValue ||
            data?.options?.response ||
            data?.type === "checkbox" ||
            data?.type === radioButtonWidget
        );
      }
    } else {
      updateItem = item.pos;
    }
    const pageNo = item.pageNumber;
    const widgetsPositionArr = updateItem;
    const pages = pdfDoc.getPages();
    const form = pdfDoc.getForm();
    const page = pages[pageNo - 1];
    // `page.getCropBox()` returns the visible area of the PDF page.
    // It provides an object with the following properties:
    // - x: the x-coordinate of the lower-left corner of the visible area
    // - y: the y-coordinate of the lower-left corner of the visible area
    // - width: the width of the visible area (x1 - x0)
    // - height: the height of the visible area (y1 - y0)
    // If the page does not explicitly define a CropBox, this method returns
    // the MediaBox instead, which represents the full physical size of the page.
    //
    // Example CropBox: [0, 7.92, 612, 799.92]
    // => x = 0, y = 7.92, width = 612 - 0 = 612, height = 799.92 - 7.92 = 792
    // This is equivalent to `react-pdf`'s `page.view` array: [x0, y0, x1, y1]
    const { _, y, width, height } = page.getCropBox();
    const originalHeight = height + y;
    let images;
    const getSize = { height: originalHeight, width: width };
    try {
      images = await Promise.all(
        widgetsPositionArr.map(async (widget) => {
          // `SignUrl` this is wrong nomenclature and maintain for older code in this options we save base64 of signature image from sign pad
          let signbase64 = widget.SignUrl && widget.SignUrl;
          const widgetDims = { Width: widget.Width, Height: widget.Height };
          if (signbase64) {
            if (!isBase64(signbase64) && prefillImg) {
              const imgData = prefillImg?.find((x) => x?.id === widget?.key);
              signbase64 = imgData?.base64;
            }

            // let arr = signbase64.split(",");
            // const mime = arr[0].match(/:(.*?);/)[1];
            const signatureImg = await convertBase64ToImg(
              signbase64,
              widgetDims
            );
            const mime = signatureImg?.split(",")?.[0]?.match(/:(.*?);/)?.[1];
            const res = await fetch(signatureImg);
            const arrayBuffer = await res.arrayBuffer();
            const obj = { mimetype: mime, arrayBuffer: arrayBuffer };
            return obj;
          }
        })
      );
    } catch (e) {
      console.log("error in image", e);
    }
    for (let [id, position] of widgetsPositionArr.entries()) {
      const isTextTypeWidget = [
        textWidget,
        textInputWidget,
        cellsWidget,
        "name",
        "company",
        "job title",
        "date",
        "email"
      ].includes(position.type);
      if (hasError) break; // Stop the inner loop if an error occurred
      try {
        let img;
        if (
          ["signature", "stamp", "initials", "image"].includes(position.type)
        ) {
          if (images[id].mimetype === "image/png") {
            img = await pdfDoc.embedPng(images[id].arrayBuffer);
          } else {
            img = await pdfDoc.embedJpg(images[id].arrayBuffer);
          }
        } else if (!position.type) {
          //  to handle old widget when only stamp and signature are exists
          if (images[id].mimetype === "image/png") {
            img = await pdfDoc.embedPng(images[id].arrayBuffer);
          } else {
            img = await pdfDoc.embedJpg(images[id].arrayBuffer);
          }
        }
        let widgetWidth, widgetHeight;
        widgetWidth = placeholderWidth(position);
        widgetHeight = placeholderHeight(position);
        const xPos = (position) => {
          const resizePos = position.xPosition;
          //first two condition handle to old data already saved from mobile view which scale point diffrent
          if (isMobile && position.isMobile) {
            //if pos.isMobile false -- placeholder saved from desktop view then handle position in mobile view divided by scale
            const x = resizePos * (position.scale / scale);
            return x * scale;
          } else if (position.isMobile && position.scale) {
            const x = resizePos * position.scale;
            return x;
          } else {
            return resizePos;
          }
        };
        const yPos = (position) => {
          const resizePos = position.yPosition;

          if (position.isMobile && position.scale) {
            if (position.IsResize) {
              const y = resizePos * position.scale;
              return y;
            } else {
              const y = resizePos * position.scale;
              return y;
            }
          } else {
            const yPosition = isTextTypeWidget ? resizePos + 6 : resizePos;
            return yPosition;
          }
        };
        const color = position?.options?.fontColor;
        const updateColorInRgb = getWidgetsFontColor(color);
        const fontSize = parseInt(position?.options?.fontSize || 12);
        if (position.type === "checkbox") {
          // Determine layout mode: 'vertical' (default) or 'horizontal'
          // const isHorizontal = position.layout === "horizontal";
          const isHorizontal =
            position?.options?.layout === "horizontal" ? true : false;
          // Initial “cursor” positions
          let currentX = xPos(position);
          let currentY = yPos(position) + 2;
          // Size and spacing settings
          const checkboxSize = fontSize - 1; // checkbox diameter
          const checkboxTextGapFromLeft = fontSize + 3.4; // gap between box and its label
          const verticalGap = fontSize + 3.4; // gap between two rows (vertical layout)
          let horizontalGap = 0; // will compute after drawing each label
          if (position?.options?.values?.length > 0) {
            position.options.values.forEach((item, ind) => {
              // 1. Advance the “cursor” on second+ iteration
              if (ind > 0) {
                if (isHorizontal) {
                  currentX += horizontalGap;
                } else {
                  currentY += verticalGap;
                }
              }
              // 2. Determine whether this checkbox should be checked
              let isCheck = false;
              if (
                position.options.response &&
                position.options.response?.length > 0
              ) {
                isCheck = position.options.response.includes(ind);
              } else if (position.options.defaultValue) {
                isCheck = position.options.defaultValue.includes(ind);
              }

              // 3. Draw the label (if labels are not hidden)
              if (!position.options.isHideLabel) {
                const labelX = currentX + checkboxTextGapFromLeft;
                const labelY = currentY - 3;

                const optionsPosition = compensateRotation(
                  page.getRotation().angle,
                  labelX,
                  labelY,
                  1,
                  getSize,
                  fontSize,
                  updateColorInRgb,
                  font,
                  page
                );
                page.drawText(item, optionsPosition);
              }
              // 4. Create and place the actual checkbox
              const checkboxRandomId = "checkbox" + randomId();
              const checkbox = form.createCheckBox(checkboxRandomId);
              let checkboxObj = {
                x: currentX,
                y: currentY,
                width: checkboxSize,
                height: checkboxSize
              };
              checkboxObj = getWidgetPosition(page, checkboxObj, 1, getSize);
              checkbox.addToPage(page, checkboxObj);
              // 5. Check or uncheck as needed, then make read‐only
              if (isCheck) {
                checkbox.check();
              } else {
                checkbox.uncheck();
              }
              checkbox.enableReadOnly();
              // 6. If horizontal layout, compute how far to shift next checkbox‐circle
              if (isHorizontal) {
                // Measure the width of this label text at `fontSize`
                const textWidth = font.widthOfTextAtSize(item, fontSize);
                // Next checkbox should come after: [box] + gap + [label text] + extra 10pt padding
                const gap = position.options?.isHideLabel
                  ? checkboxTextGapFromLeft - 5
                  : checkboxTextGapFromLeft + textWidth;
                horizontalGap = checkboxSize + gap;
              }
            });
          }
        } else if (isTextTypeWidget) {
          let textContent = "";
          if (position?.options?.response) {
            if (
              position.type === "date" &&
              position.options?.response === "today"
            ) {
              const getFormat = changeDateToMomentFormat(
                position?.options?.validation?.format
              );
              textContent = moment().format(getFormat);
            } else {
              textContent = position.options?.response;
            }
          } else if (position?.options?.defaultValue) {
            textContent = position?.options?.defaultValue?.toString();
          }
          if (position.type === cellsWidget) {
            const cellCount =
              position?.options?.cellCount || textContent.length || 1;
            const charWidth = widgetWidth / cellCount;
            const y = yPos(position) - 4;
            for (let i = 0; i < cellCount; i++) {
              const ch = textContent[i] || "";
              const charX =
                xPos(position) +
                charWidth * i +
                (charWidth - font.widthOfTextAtSize(ch, fontSize)) / 2;
              const textPosition = compensateRotation(
                page.getRotation().angle,
                charX,
                y,
                1,
                getSize,
                fontSize,
                updateColorInRgb,
                font,
                page
              );
              if (ch) page.drawText(ch, textPosition);
            }
          } else {
            const fixedWidth = widgetWidth; // Set your fixed
            textContent = textContent?.toString();
            const isNewOnEnterLineExist = textContent?.includes("\n");

            // Function to break text into lines based on the fixed width
            const NewbreakTextIntoLines = (textContent, width) => {
              const lines = [];
              let currentLine = "";

              for (const word of textContent?.split(" ")) {
                //get text line width
                const lineWidth = font.widthOfTextAtSize(
                  `${currentLine} ${word}`,
                  fontSize
                );
                //check text content line width is less or equal to container width
                if (lineWidth <= width) {
                  currentLine += ` ${word}`;
                } else {
                  lines.push(currentLine.trim());
                  currentLine = `${word}`;
                }
              }
              lines.push(currentLine.trim());
              return lines;
            };
            // Function to break text into lines based on when user go next line on press enter button
            const breakTextIntoLines = (textContent, width) => {
              const lines = [];
              for (const word of textContent.split("\n")) {
                const lineWidth = font.widthOfTextAtSize(`${word}`, fontSize);
                //checking string length to container width
                //if string length is less then container width it means user press enter button
                if (lineWidth <= width) {
                  lines.push(word);
                }
                //else adjust text content according to width and send it in new line
                else {
                  const newLine = NewbreakTextIntoLines(word, width);
                  lines.push(...newLine);
                }
              }

              return lines;
            };
            //check if text content have `\n` string it means user press enter to go next line and handle condition
            //else auto adjust text content according to container width
            const lines = isNewOnEnterLineExist
              ? breakTextIntoLines(textContent, fixedWidth)
              : NewbreakTextIntoLines(textContent, fixedWidth);
            // Set initial y-coordinate for the first line
            let x = xPos(position);
            let y = yPos(position) - 4;
            // Embed each line on the page
            for (const line of lines) {
              const textPosition = compensateRotation(
                page.getRotation().angle,
                x,
                y,
                1,
                getSize,
                fontSize,
                updateColorInRgb,
                font,
                page
              );
              page.drawText(line, textPosition);
              y += 18; // Adjust the line height as needed
            }
          }
        } else if (position.type === "dropdown") {
          const dropdownRandomId = "dropdown" + randomId();
          const dropdown = form.createDropdown(dropdownRandomId);
          dropdown.addOptions(position?.options?.values);
          if (position?.options?.response) {
            dropdown.select(position.options?.response);
          } else if (position?.options?.defaultValue) {
            dropdown.select(position?.options?.defaultValue);
          }
          // Define the default appearance string
          // Example format: `/FontName FontSize Tf 0 g` where:
          // - `/FontName` is the name of the font (e.g., `/Helv` for Helvetica)
          // - `FontSize` is the size you want to set (e.g., 12)
          // - `Tf` specifies the font and size
          // - `0 g` sets the text color to black
          const defaultAppearance = `/Helv ${fontSize} Tf 0 g`;
          // Set the default appearance for the dropdown field
          dropdown.acroField.setDefaultAppearance(defaultAppearance);
          dropdown.setFontSize(fontSize);
          const dropdownObj = {
            x: xPos(position),
            y: yPos(position),
            width: widgetWidth,
            height: widgetHeight
          };
          const dropdownOption = getWidgetPosition(
            page,
            dropdownObj,
            1,
            getSize
          );
          const dropdownSelected = { ...dropdownOption, font: font };
          dropdown.defaultUpdateAppearances(font);
          dropdown.addToPage(page, dropdownSelected);
          dropdown.enableReadOnly();
        } else if (position.type === radioButtonWidget) {
          const radioRandomId = "radio" + randomId();
          const radioGroup = form.createRadioGroup(radioRandomId);
          //getting radio buttons options text font size
          const optionsFontSize = fontSize; // font size for option text
          const radioTextGapFromLeft = fontSize + 3; // gap between circle and its label
          const radioSize = fontSize; // circle diameter (square of width×height)
          // Initial “cursor” positions (from your existing helpers)
          let currentX = xPos(position) + 2;
          let currentY = yPos(position) + 3;
          // Vertical gap between two radio‐rows
          const verticalGap = fontSize + 3;
          // We’ll compute horizontalGap on the fly—after drawing each label
          // Initialize to zero (will be set after first option is placed)
          let horizontalGap = 0;
          // Determine layout mode: 'vertical' or 'horizontal'.
          // (You mentioned “add one variable called layout” – here we read it from position.layout.)
          const isHorizontal =
            position?.options?.layout === "horizontal" ? true : false;
          // Loop through each option in the group
          if (position?.options?.values?.length > 0) {
            position.options.values.forEach((item, ind) => {
              // 1. Advance cursor on second+ iteration
              if (ind > 0) {
                if (isHorizontal) {
                  // Move to the right by horizontalGap
                  currentX += horizontalGap;
                } else {
                  // Move down by verticalGap (vertical stacking)
                  currentY += verticalGap;
                }
              }
              // 2. Draw the label text (if not hidden)
              if (!position?.options?.isHideLabel) {
                // Compute where to draw the text (just to the right of the circle)
                const labelX = currentX + radioTextGapFromLeft;
                const labelY = currentY - 2;
                const optionsPosition = compensateRotation(
                  page.getRotation().angle,
                  labelX,
                  labelY,
                  1,
                  getSize,
                  optionsFontSize,
                  updateColorInRgb,
                  font,
                  page
                );

                page.drawText(item, optionsPosition);
              }
              // 3. Place the radio‐circle itself at (currentX, currentY)
              let radioObj = {
                x: currentX,
                y: currentY,
                width: radioSize,
                height: radioSize
              };

              radioObj = getWidgetPosition(page, radioObj, 1, getSize);
              radioGroup.addOptionToPage(item, page, radioObj);
              // 4. If horizontal layout, re-compute horizontalGap for next iteration:
              if (isHorizontal) {
                // Measure how wide the label text is, so we know how far to shift next circle
                const textWidth = font.widthOfTextAtSize(item, optionsFontSize);
                // radioSize = the circle.  radioTextGapFromLeft = gap between circle and label.
                // Add a small extra padding (e.g. 10pt) before placing next circle.
                const gap = position?.options?.isHideLabel
                  ? radioTextGapFromLeft - 6
                  : radioTextGapFromLeft + textWidth;
                horizontalGap = radioSize + gap;
              }
            });
          }
          // 5. Pre‐select a value if provided
          if (position?.options?.response) {
            radioGroup.select(position.options?.response);
          } else if (position?.options?.defaultValue) {
            radioGroup.select(position?.options?.defaultValue);
          }
          // 6. Set to read‐only (if required)
          radioGroup.enableReadOnly();
        } else {
          const signature = {
            x: xPos(position),
            y: yPos(position),
            width: widgetWidth,
            height: widgetHeight
          };
          const imageOptions = getWidgetPosition(page, signature, 1, getSize);
          page.drawImage(img, imageOptions);
        }
      } catch (err) {
        console.log("Err in embed widget on page ", pageNo, err);
        hasError = true; // Set the flag to stop both loops
        break; // Exit inner loop
      }
    }
    form?.flatten();
  }
  if (!hasError) {
    const pdfBytes = await pdfDoc.saveAsBase64({ useObjectStreams: false });
    return pdfBytes;
  } else {
    return {
      error:
        "This pdf is not compatible with opensign please contact <support@opensignlabs.com>"
    };
  }
};

// function for validating URLs
export function urlValidator(url) {
  try {
    const newUrl = new URL(url);
    return newUrl.protocol === "http:" || newUrl.protocol === "https:";
  } catch (err) {
    return false;
  }
}
//calculate placeholder width to embed in pdf
export const placeholderWidth = (pos) => {
  const defaultWidth = defaultWidthHeight(pos.type).width;
  const posWidth = pos.Width || defaultWidth;
  //condition to handle old data saved from mobile view to get widthh
  if (pos.isMobile && pos.scale) {
    if (pos.IsResize) {
      return posWidth;
    } else {
      return posWidth * pos.scale;
    }
  } else {
    return posWidth;
  }
};

//calculate placeholder height to embed in pdf
export const placeholderHeight = (pos) => {
  const posHeight = pos.Height;
  const defaultHeight = defaultWidthHeight(pos.type).height;
  const posUpdateHeight = posHeight || defaultHeight;

  //condition to handle old data saved from mobile view to get height
  if (pos.isMobile && pos.scale) {
    if (pos.IsResize) {
      return posUpdateHeight;
    } else {
      return posUpdateHeight * pos.scale;
    }
  } else {
    return posUpdateHeight;
  }
};

//function for getting contracts_contactbook details
export const contactBook = async (objectId) => {
  const result = await axios
    .get(
      `${localStorage.getItem(
        "baseUrl"
      )}classes/contracts_Contactbook?where={"objectId":"${objectId}"}`,
      {
        headers: {
          "Content-Type": "application/json",
          "X-Parse-Application-Id": localStorage.getItem("parseAppId"),
          "X-Parse-Session-Token": localStorage.getItem("accesstoken")
        }
      }
    )
    .then((Listdata) => {
      const json = Listdata.data;
      const res = json.results;
      return res;
    })

    .catch((err) => {
      console.log("Err in contracts_Contactbook class ", err);
      return "Error: Something went wrong!";
    });
  return result;
};

//function for getting document details from contract_Documents class
export const contractDocument = async (documentId, include) => {
  const data = { docId: documentId, include: include };
  const token = { sessionToken: localStorage.getItem("accesstoken") };
  const documentDeatils = await axios
    .post(`${localStorage.getItem("baseUrl")}functions/getDocument`, data, {
      headers: {
        "Content-Type": "application/json",
        "X-Parse-Application-Id": localStorage.getItem("parseAppId"),
        ...token
      }
    })
    .then((Listdata) => {
      const json = Listdata.data;
      let data = [];
      if (json && json.result.error) {
        return json;
      } else if (json && json.result) {
        data.push(json.result);
        return data;
      } else {
        return [];
      }
    })
    .catch((err) => {
      console.log("Err in getDocument cloud function ", err);
      return "Error: Something went wrong!";
    });

  return documentDeatils;
};

//function for add default signature or image for all requested location
export const addDefaultSignatureImg = (xyPosition, defaultSignImg, type) => {
  let xyDefaultPos = [];
  for (let i = 0; i < xyPosition.length; i++) {
    const getXYdata = xyPosition[i].pos;
    const getPageNo = xyPosition[i].pageNumber;
    const getPosData = getXYdata;

    const addSign = getPosData.map((position) => {
      if (position.type) {
        if (position?.type === type) {
          return {
            ...position,
            SignUrl: defaultSignImg,
            ImageType: "default",
            options: { ...position.options, response: defaultSignImg }
          };
        }
      } else if (position && !position.isStamp) {
        return {
          ...position,
          SignUrl: defaultSignImg,
          ImageType: "default",
          options: { ...position.options, response: defaultSignImg }
        };
      }
      return position;
    });

    const newXypos = { pageNumber: getPageNo, pos: addSign };
    xyDefaultPos.push(newXypos);
  }
  return xyDefaultPos;
};

//function for get month
export const getMonth = (date) => {
  const newMonth = new Date(date).getMonth();
  return newMonth;
};
//function to create/copy widget next to already dropped widget
export const handleCopyNextToWidget = (
  newId,
  position,
  xyPosition,
  index,
  setXyPosition,
  userId
) => {
  let filterSignerPos;
  //get position of previous widget and create new widget next to that widget on same data except
  // xPosition and key
  let newposition = position;
  const calculateXPosition = parseInt(position.xPosition) + 10;
  const calculateYPosition = parseInt(position.yPosition) + 10;
  const widgetName = `${newposition?.options?.name}${randomId(2)}`;
  newposition = {
    ...newposition,
    xPosition: calculateXPosition,
    yPosition: calculateYPosition,
    key: newId,
    options: { ...newposition?.options, name: widgetName }
  };
  //if condition to create widget in request-sign flow
  if (userId) {
    filterSignerPos = xyPosition.find((data) => data.Id === userId);
    const getPlaceHolder = filterSignerPos && filterSignerPos?.placeHolder;
    const getPageNumer = getPlaceHolder?.filter(
      (data) => data.pageNumber === index
    );
    const getXYdata = getPageNumer && getPageNumer[0]?.pos;
    getXYdata.push(newposition);
    if (getPageNumer && getPageNumer.length > 0) {
      const newUpdateSignPos = getPlaceHolder.map((obj) => {
        if (obj.pageNumber === index) {
          return { ...obj, pos: getXYdata };
        }
        return obj;
      });

      const newUpdateSigner = xyPosition.map((obj) => {
        if (obj.Id === userId) {
          return { ...obj, placeHolder: newUpdateSignPos };
        }
        return obj;
      });

      setXyPosition(newUpdateSigner);
    }
  } else {
    let getXYdata = xyPosition[index]?.pos || [];
    getXYdata.push(newposition);
    const updatePlaceholder = xyPosition.map((obj, ind) => {
      if (ind === index) {
        return { ...obj, pos: getXYdata };
      }
      return obj;
    });

    setXyPosition(updatePlaceholder);
  }
};

export const getFileName = (fileUrl) => {
  if (fileUrl) {
    const url = new URL(fileUrl);
    const filename = url.pathname.substring(url.pathname.indexOf("_") + 1);
    return filename || "";
  } else {
    return "";
  }
};

//fetch tenant app logo from `partners_Tenant` class by domain name
export const getAppLogo = async () => {
  const domain = window.location.host;
  try {
    const tenant = await Parse.Cloud.run("getlogobydomain", {
      domain: domain
    });
    if (tenant) {
      localStorage.setItem("appname", "OpenSign™");
      localStorage.setItem("favicon", appInfo.fev_Icon);
      return {
        logo: tenant?.logo,
        user: tenant?.user
      };
    }
  } catch (err) {
    console.log("err in getlogo ", err);
    localStorage.setItem("favicon", appInfo.fev_Icon);
    if (err?.message?.includes("valid JSON")) {
      return { logo: appInfo.applogo, user: "exist", error: "invalid_json" };
    } else {
      return { logo: appInfo.applogo, user: "exist" };
    }
  }
};
export const getTenantDetails = async (objectId, contactId) => {
  try {
    const url = `${localStorage.getItem("baseUrl")}functions/gettenant`;
    const parseAppId = localStorage.getItem("parseAppId");
    const accesstoken = localStorage.getItem("accesstoken");
    const token = { "X-Parse-Session-Token": accesstoken };
    const data = { userId: objectId, contactId: contactId };
    const res = await axios.post(url, data, {
      headers: {
        "Content-Type": "application/json",
        "X-Parse-Application-Id": parseAppId,
        ...token
      }
    });
    if (res.data.result) {
      const updateRes = JSON.parse(JSON.stringify(res.data.result));
      return updateRes;
    } else {
      return "";
    }
  } catch (err) {
    console.log("err in gettenant", err);
    return "user does not exist!";
  }
};

//function to convert variable string name to variable value of email body and subject
export function replaceMailVaribles(subject, body, variables) {
  let replacedSubject = subject;
  let replacedBody = body;

  for (const variable in variables) {
    const regex = new RegExp(`{{${variable}}}`, "g");
    if (subject) {
      replacedSubject = replacedSubject.replace(regex, variables[variable]);
    }
    if (body) {
      replacedBody = replacedBody.replace(regex, variables[variable]);
    }
  }

  const result = {
    subject: replacedSubject,
    body: replacedBody
  };
  return result;
}

export const copytoData = (url) => {
  // navigator.clipboard.writeText(text);
  if (navigator.clipboard) {
    navigator.clipboard.writeText(url);
  } else {
    // Fallback for browsers that don't support navigator.clipboard
    const textArea = document.createElement("textarea");
    textArea.value = url;
    document.body.appendChild(textArea);
    textArea.select();
    document.execCommand("copy");
    document.body.removeChild(textArea);
  }
};

export const convertPdfArrayBuffer = async (url) => {
  try {
    const response = await fetch(url);
    // Check if the response was successful (status 200)
    if (!response.ok) {
      return "Error";
    }
    // Convert the response to ArrayBuffer
    const arrayBuffer = await response.arrayBuffer();
    return arrayBuffer;
  } catch (error) {
    console.error("Error fetching data:", error);
    return "Error";
  }
};
//`handleSendOTP` function is used to send otp on user's email using `SendOTPMailV1` cloud function
export const handleSendOTP = async (email) => {
  try {
    let url = `${localStorage.getItem("baseUrl")}functions/SendOTPMailV1`;
    const headers = {
      "Content-Type": "application/json",
      "X-Parse-Application-Id": localStorage.getItem("parseAppId")
    };
    const body = {
      email: email
    };
    await axios.post(url, body, { headers: headers });
  } catch (error) {
    alert(error.message);
  }
};
export const fetchUrl = async (url, fileName) => {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      alert(i18n.t("something-went-wrong-mssg"));
      throw new Error("Network response was not ok");
    }
    const blob = await response.blob();
    saveAs(blob, fileName);
  } catch (error) {
    alert(i18n.t("something-went-wrong-mssg"));
    console.error("Error downloading the file:", error);
  }
};

export const getSignedUrl = async (pdfUrl, docId, templateId) => {
  //use only axios here due to public template sign
  const axiosRes = await axios.post(
    `${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
    {
      url: pdfUrl,
      docId: docId || "",
      templateId: templateId || ""
    },
    {
      headers: {
        "content-type": "Application/json",
        "X-Parse-Application-Id": localStorage.getItem("parseAppId"),
        "X-Parse-Session-Token": localStorage.getItem("accesstoken")
      }
    }
  );
  const url = axiosRes.data.result;
  return url;
};
//download base64 type pdf
export const fetchBase64 = async (pdfBase64, pdfName) => {
  // Create a Blob from the Base64 string
  const byteCharacters = atob(pdfBase64);
  const byteNumbers = new Array(byteCharacters.length)
    .fill(0)
    .map((_, i) => byteCharacters.charCodeAt(i));
  const byteArray = new Uint8Array(byteNumbers);
  const blob = new Blob([byteArray], { type: "application/pdf" });

  // Create a link element
  const link = document.createElement("a");
  link.href = URL.createObjectURL(blob);
  link.download = pdfName;

  // Programmatically click the link to trigger the download
  document.body.appendChild(link);
  link.click();

  // Clean up
  document.body.removeChild(link);
  URL.revokeObjectURL(link.href);
};
//handle download signed pdf
export const handleDownloadPdf = async (
  pdfDetails,
  setIsDownloading,
  pdfBase64
) => {
  const pdfName =
    pdfDetails?.[0]?.Name?.length > 100
      ? pdfDetails?.[0]?.Name?.slice(0, 100)
      : pdfDetails?.[0]?.Name || "Document";
  const isCompleted = pdfDetails?.[0]?.IsCompleted || false;
  const formatId = pdfDetails?.[0]?.ExtUserPtr?.DownloadFilenameFormat;
  const docName = buildDownloadFilename(formatId, {
    docName: pdfName,
    email: pdfDetails?.[0]?.ExtUserPtr?.Email,
    isSigned: isCompleted
  });
  if (pdfBase64) {
    await fetchBase64(pdfBase64, docName);
    setIsDownloading && setIsDownloading("");
  } else {
    const pdfUrl = pdfDetails?.[0]?.SignedUrl || pdfDetails?.[0]?.URL;
    setIsDownloading && setIsDownloading("pdf");
    const docId = pdfDetails?.[0]?.objectId || "";
    try {
      const url = await getSignedUrl(pdfUrl, docId);
      await fetchUrl(url, docName);
      setIsDownloading && setIsDownloading("");
    } catch (err) {
      console.log("err in getsignedurl", err);
      setIsDownloading("");
      alert(i18n.t("something-went-wrong-mssg"));
    }
  }
};

export function fileNameWithUnderscore(pdfName) {
  // Replace spaces with underscore
  return pdfName.replace(/ /g, "_");
}
//function for print digital sign pdf
export const handleToPrint = async (event, setIsDownloading, pdfDetails) => {
  event.preventDefault();
  setIsDownloading("pdf");
  const pdfUrl = pdfDetails?.[0]?.SignedUrl || pdfDetails?.[0]?.URL;
  const docId = pdfDetails?.[0]?.objectId || "";

  try {
    // const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
    //`localStorage.getItem("baseUrl")` is also use in public-profile flow for public-sign
    //if we give this `appInfo.baseUrl` as a base url then in public-profile it will create base url of it's window.location.origin ex- opensign.me which is not base url
    const axiosRes = await axios.post(
      `${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
      {
        url: pdfUrl,
        docId: docId
      },
      {
        headers: {
          "content-type": "Application/json",
          "X-Parse-Application-Id": localStorage.getItem("parseAppId"),
          "X-Parse-Session-Token": localStorage.getItem("accesstoken")
        }
      }
    );
    const url = axiosRes.data.result;
    const pdf = await getBase64FromUrl(url);
    const isAndroidDevice = navigator.userAgent.match(/Android/i);
    const isAppleDevice =
      (/iPad|iPhone|iPod/.test(navigator.platform) ||
        (navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1)) &&
      !window.MSStream;
    if (isAndroidDevice || isAppleDevice) {
      const byteArray = Uint8Array.from(
        atob(pdf)
          .split("")
          .map((char) => char.charCodeAt(0))
      );
      const blob = new Blob([byteArray], { type: "application/pdf" });
      const blobUrl = URL.createObjectURL(blob);
      window.open(blobUrl, "_blank");
      setIsDownloading("");
    } else {
      printModule({ printable: pdf, type: "pdf", base64: true });
      setIsDownloading("");
    }
  } catch (err) {
    setIsDownloading("");
    console.log("err in getsignedurl", err);
    alert(i18n.t("something-went-wrong-mssg"));
  }
};

//handle download signed pdf
export const handleDownloadCertificate = async (
  pdfDetails,
  setIsDownloading,
  isZip
) => {
  const appName = "OpenSign™";
  if (pdfDetails?.length > 0 && pdfDetails[0]?.CertificateUrl) {
    try {
      await fetch(pdfDetails[0] && pdfDetails[0]?.CertificateUrl);
      const certificateUrl = pdfDetails[0] && pdfDetails[0]?.CertificateUrl;
      if (isZip) {
        return certificateUrl;
      } else {
        saveAs(certificateUrl, `Certificate_signed_by_${appName}.pdf`);
      }
    } catch (err) {
      console.log("err in download in certificate", err);
    }
  } else {
    setIsDownloading("certificate");
    try {
      const data = { docId: pdfDetails[0]?.objectId };
      const docDetails = await axios.post(
        `${localStorage.getItem("baseUrl")}functions/getDocument`,
        data,
        {
          headers: {
            "Content-Type": "application/json",
            "X-Parse-Application-Id": localStorage.getItem("parseAppId"),
            sessionToken: localStorage.getItem("accesstoken")
          }
        }
      );
      if (docDetails.data && docDetails.data.result) {
        const doc = docDetails.data.result;
        if (doc?.CertificateUrl) {
          await fetch(doc?.CertificateUrl);
          const certificateUrl = doc?.CertificateUrl;
          if (isZip) {
            setIsDownloading("");
            return certificateUrl;
          } else {
            saveAs(certificateUrl, `Certificate_signed_by_${appName}.pdf`);
            setIsDownloading("");
          }
        } else {
          const generateRes = await axios.post(
            `${localStorage.getItem("baseUrl")}functions/generatecertificate`,
            data,
            {
              headers: {
                "Content-Type": "application/json",
                "X-Parse-Application-Id": localStorage.getItem("parseAppId")
              }
            }
          );
          if (generateRes?.data?.result?.CertificateUrl) {
            try {
              const certificateUrl = generateRes.data.result.CertificateUrl;
              const fetchCertificate = await fetch(certificateUrl);
              if (isZip) {
                setIsDownloading("");
                return certificateUrl;
              } else {
                // Convert the response into a Blob
                const certificateBlob = await fetchCertificate.blob();
                setIsDownloading("");
                saveAs(certificateBlob, `Certificate_signed_by_${appName}.pdf`);
              }
            } catch (err) {
              console.log("err in download in certificate", err);
              setIsDownloading("certificate_err");
            }
          } else {
            setIsDownloading("certificate_err");
          }
        }
      }
    } catch (err) {
      setIsDownloading("certificate_err");
      console.log("err in download in certificate", err);
      alert(i18n.t("something-went-wrong-mssg"));
    }
  }
};
// Function to escape special characters in the search string
export function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // Escape special characters
}
export async function findContact(value) {
  try {
    const baseURL = localStorage.getItem("baseUrl");
    const url = `${baseURL}functions/getsigners`;
    const token = {
      "X-Parse-Session-Token": localStorage.getItem("accesstoken")
    };
    const headers = {
      "Content-Type": "application/json",
      "X-Parse-Application-Id": localStorage.getItem("parseAppId"),
      ...token
    };
    const axiosRes = await axios.post(url, { search: value }, { headers });
    const contactRes = axiosRes?.data?.result || [];
    if (contactRes) {
      const res = JSON.parse(JSON.stringify(contactRes));
      return res;
    }
  } catch (error) {
    console.error("Error fetching suggestions:", error);
  }
}
// `compensateRotation` is used to calculate x and y position of widget on portait, landscape pdf for pdf-lib
function compensateRotation(
  pageRotation,
  x,
  y,
  scale,
  dimensions,
  fontSize,
  updateColorInRgb,
  font,
  page
) {
  // Ensure pageRotation is between 0 and 360 degrees
  pageRotation = ((pageRotation % 360) + 360) % 360;
  let rotationRads = (pageRotation * Math.PI) / 180;

  // Coordinates are from bottom-left
  let coordsFromBottomLeft = { x: x / scale };
  if (pageRotation === 90 || pageRotation === 270) {
    coordsFromBottomLeft.y = dimensions.width - (y + fontSize) / scale;
  } else {
    coordsFromBottomLeft.y = dimensions.height - (y + fontSize) / scale;
  }

  let drawX = null;
  let drawY = null;

  if (pageRotation === 90) {
    drawX =
      coordsFromBottomLeft.x * Math.cos(rotationRads) -
      coordsFromBottomLeft.y * Math.sin(rotationRads) +
      dimensions.width;
    drawY =
      coordsFromBottomLeft.x * Math.sin(rotationRads) +
      coordsFromBottomLeft.y * Math.cos(rotationRads);
  } else if (pageRotation === 180) {
    drawX =
      coordsFromBottomLeft.x * Math.cos(rotationRads) -
      coordsFromBottomLeft.y * Math.sin(rotationRads) +
      dimensions.width;
    drawY =
      coordsFromBottomLeft.x * Math.sin(rotationRads) +
      coordsFromBottomLeft.y * Math.cos(rotationRads) +
      dimensions.height;
  } else if (pageRotation === 270) {
    drawX =
      coordsFromBottomLeft.x * Math.cos(rotationRads) -
      coordsFromBottomLeft.y * Math.sin(rotationRads);
    drawY =
      coordsFromBottomLeft.x * Math.sin(rotationRads) +
      coordsFromBottomLeft.y * Math.cos(rotationRads) +
      dimensions.height;
  } else if (pageRotation === 0 || pageRotation === 360) {
    // No rotation or full rotation
    drawX = coordsFromBottomLeft.x;
    drawY = coordsFromBottomLeft.y;
  }
  if (font) {
    return {
      x: drawX,
      y: drawY,
      font,
      color: updateColorInRgb,
      size: fontSize,
      rotate: page.getRotation()
    };
  } else {
    return { x: drawX, y: drawY };
  }
}

// `getWidgetPosition` is used to calulcate position of image type widget like x, y, width, height for pdf-lib
function getWidgetPosition(page, image, sizeRatio, getSize) {
  let pageWidth;
  // pageHeight;
  if ([90, 270].includes(page.getRotation().angle)) {
    pageWidth = getSize?.height || page.getHeight();
  } else {
    pageWidth = getSize?.width || page.getWidth();
  }
  // eslint-disable-next-line
  if (!image?.hasOwnProperty("vpWidth")) {
    image["vpWidth"] = pageWidth;
  }
  const pageRatio = pageWidth / (image.vpWidth * sizeRatio);
  const imageWidth = image.width * sizeRatio * pageRatio;
  const imageHeight = image.height * sizeRatio * pageRatio;
  const imageX = image.x * sizeRatio * pageRatio;
  const imageYFromTop = image.y * sizeRatio * pageRatio;

  const correction = compensateRotation(
    page.getRotation().angle,
    imageX,
    imageYFromTop,
    1,
    getSize,
    imageHeight
  );

  return {
    width: imageWidth,
    height: imageHeight,
    x: correction.x,
    y: correction.y,
    rotate: page.getRotation()
  };
}
//function to use calculate pdf rendering scale in the container
export const getContainerScale = (pdfOriginalWH, pageNumber, containerWH) => {
  const getPdfPageWidth = pdfOriginalWH.find(
    (data) => data.pageNumber === pageNumber
  );
  const containerScale = containerWH?.width / getPdfPageWidth?.width || 1;
  return containerScale;
};

//function to get current laguage and set it in local
export const saveLanguageInLocal = (i18n) => {
  const detectedLanguage = i18n.language || "en";
  localStorage.setItem("i18nextLng", detectedLanguage);
};

// function to get default signature of current user from `contracts_Signature` class
export const getDefaultSignature = async (objectId) => {
  try {
    if (objectId) {
      const result = await Parse.Cloud.run("getdefaultsignature", {
        userId: objectId
      });
      if (result) {
        const res = JSON.parse(JSON.stringify(result));
        const defaultSignature = res?.ImageURL
          ? await getBase64FromUrl(res?.ImageURL, true)
          : "";
        const defaultInitial = res?.Initials
          ? await getBase64FromUrl(res?.Initials, true)
          : "";

        return {
          status: "success",
          res: {
            id: result?.id,
            defaultSignature: defaultSignature,
            defaultInitial: defaultInitial
          }
        };
      }
    } else {
      return { status: "error" };
    }
  } catch (err) {
    console.log(
      "Error: error in fetch data in contracts_Signature",
      err?.message || err
    );
    return { status: "error" };
  }
};

//function to rotate pdf page
export async function rotatePdfPage(rotateDegree, pageNumber, pdfArrayBuffer) {
  // Load the existing PDF
  const pdfDoc = await PDFDocument.load(pdfArrayBuffer);
  // Get the page according to page number
  const page = pdfDoc.getPage(pageNumber);
  //get current page rotation angle
  const currentRotation = page.getRotation().angle;
  // Apply the rotation in the counterclockwise direction
  let newRotation = (currentRotation + rotateDegree) % 360;
  // Adjust for negative angles to keep within 0-359 range
  if (newRotation < 0) {
    newRotation += 360;
  }
  page.setRotation(degrees(newRotation));
  const pdfbase64 = await pdfDoc.saveAsBase64({ useObjectStreams: false });
  //convert base64 to arraybuffer is used in pdf-lib
  //pdfbase64 is used to show pdf rotated format
  const arrayBuffer = base64ToArrayBuffer(pdfbase64);
  //`base64` is used to show pdf
  return { arrayBuffer: arrayBuffer, base64: pdfbase64 };
}
export function base64ToArrayBuffer(base64) {
  // Decode the base64 string to a binary string
  const binaryString = atob(base64);
  // Create a new ArrayBuffer with the same length as the binary string
  const len = binaryString.length;
  const bytes = new Uint8Array(len);
  // Convert the binary string to a byte array
  for (let i = 0; i < len; i++) {
    bytes[i] = binaryString.charCodeAt(i);
  }
  // Return the ArrayBuffer
  return bytes.buffer;
}
export function removeBase64Prefix(base64String) {
  return base64String.replace(/^data:.*;base64,/, "");
}

export const convertBase64ToFile = async (pdfName, pdfBase64, imgType) => {
  let base64Str = pdfBase64;
  const mime = imgType && imgType?.split("/")?.pop();
  let fileName =
    fileNameWithUnderscore(pdfName) + (imgType ? `.${mime}` : ".pdf");
  try {
    fileName = imgType ? pdfName : fileName;
    const pdfFile = new Parse.File(fileName, { base64: base64Str });
    // Save the Parse File if needed
    const pdfData = await pdfFile.save();
    const pdfUrl = pdfData.url();
    const fileRes = await getSecureUrl(pdfUrl);
    if (fileRes?.url) {
      return fileRes.url;
    }
  } catch (e) {
    console.log("error in convertbase64tofile", e);
  }
};
export const onClickZoomIn = (scale, zoomPercent, setScale, setZoomPercent) => {
  const newPercent = zoomPercent + 10;
  setZoomPercent(newPercent);
  setScale(1 + newPercent / 100);
};
export const onClickZoomOut = (
  zoomPercent,
  scale,
  setZoomPercent,
  setScale
) => {
  if (zoomPercent > 0) {
    const newPercent = Math.max(0, zoomPercent - 10);
    setZoomPercent(newPercent);
    setScale(1 + newPercent / 100);
  }
};

//function to use remove widgets from current page when user want to rotate page
export const handleRemoveWidgets = (
  setSignerPos,
  signerPos,
  pageNumber,
  setIsRotate
) => {
  const isSigners = signerPos.some((data) => data.signerPtr);
  //placeholder,template,draftTemplate flow
  if (isSigners) {
    const updatedSignerPos = signerPos.map((placeholderObj) => {
      return {
        ...placeholderObj,
        placeHolder: placeholderObj?.placeHolder?.filter(
          (data) => data?.pageNumber !== pageNumber
        )
      };
    });

    if (setIsRotate) {
      setSignerPos(updatedSignerPos);
      setIsRotate({ status: false, degree: 0 });
    } else {
      //after deleting pdf page we need to update page number of widgets
      //For example, consider a PDF with 3 pages where widgets are placed on the 2nd page.
      //If we delete the 1st page, the total number of pages will be reduced to 2. In this case,
      // the widgets need to be updated to reflect the new page numbering and should now appear on the 1st page.
      const updatePageNumber = updatedSignerPos?.map((placeholderObj) => {
        return {
          ...placeholderObj,
          placeHolder: placeholderObj?.placeHolder?.map((data) => {
            if (data.pageNumber > pageNumber) {
              return { ...data, pageNumber: data.pageNumber - 1 };
            } else {
              return data;
            }
          })
        };
      });
      setSignerPos(updatePageNumber);
    }
  } else {
    //signyourself flow
    const updatedSignerPos = signerPos?.filter(
      (data) => data?.pageNumber !== pageNumber
    );
    if (setIsRotate) {
      setSignerPos(updatedSignerPos);
      setIsRotate({ status: false, degree: 0 });
    } else {
      //after deleting pdf page we need to update page number of widgets
      //For example, consider a PDF with 3 pages where widgets are placed on the 2nd page.
      //If we delete the 1st page, the total number of pages will be reduced to 2. In this case,
      // the widgets need to be updated to reflect the new page numbering and should now appear on the 1st page.
      const updatePageNumber = updatedSignerPos?.map((data) => {
        if (data.pageNumber > pageNumber) {
          return { ...data, pageNumber: data.pageNumber - 1 };
        } else {
          return data;
        }
      });
      setSignerPos(updatePageNumber);
    }
  }
};
//function to show warning when user rotate page and there are some already widgets on that page
export const handleRotateWarning = (signerPos, pageNumber) => {
  const placeholderExist = signerPos?.some((placeholderObj) =>
    placeholderObj?.placeHolder?.some((data) => data?.pageNumber === pageNumber)
  );
  if (placeholderExist) {
    return true;
  } else {
    return false;
  }
};

// `generateTitleFromFilename` to generate Title of document from file name
export function generateTitleFromFilename(filename) {
  try {
    // Step 1: Trim whitespace
    let title = filename.trim();

    // Step 2: Remove the file extension (everything after the last '.')
    const lastDotIndex = title.lastIndexOf(".");
    if (lastDotIndex > 0) {
      title = title.substring(0, lastDotIndex);
    }

    // Step 3: Replace special characters (except Unicode letters, digits, spaces, and hyphens)
    title = title.replace(/[^\p{L}\p{N}\s-]/gu, " ");

    // Step 4: Replace multiple spaces with a single space
    title = title.replace(/\s+/g, " ");

    // Step 5: Capitalize first letter of each word (Title Case), handling Unicode characters
    title = title.replace(
      /\p{L}\S*/gu,
      (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()
    );

    // Step 6: Restrict length of title (optional, let's say 100 characters)
    if (title.length > 100) {
      title = title.substring(0, 100).trim();
    }

    // Step 7: Handle empty or invalid title by falling back to "Untitled Document"
    if (!title || title.length === 0) {
      return "Untitled Document";
    }

    return title;
  } catch (error) {
    // Handle unexpected errors gracefully by returning a default title
    console.error("Error generating title from filename:", error);
    return "Untitled Document";
  }
}

export const signatureTypes = [
  { name: "draw", enabled: true },
  { name: "typed", enabled: true },
  { name: "upload", enabled: true },
  { name: "default", enabled: true }
];

// `handleSignatureType` is used to return update signature types as per tenant or user
export async function handleSignatureType(tenantSignTypes, signatureType) {
  const docSignTypes = signatureType || signatureTypes;
  let updatedSignatureType = signatureType || signatureTypes;
  if (tenantSignTypes?.length > 0) {
    updatedSignatureType = tenantSignTypes?.map((item) => {
      const match = docSignTypes.find((data) => data.name === item.name);
      return match ? { ...item, enabled: match.enabled } : item;
    });
  }
  return updatedSignatureType;
}

// `formatDate` is used to format date to dd-mmm-yyy
export const formatDate = (date) => {
  // Create a Date object
  const newDate = new Date(date);
  // Format the date
  const formattedDate = newDate.toLocaleDateString("en-GB", {
    day: "2-digit",
    month: "short",
    year: "numeric"
  });
  const format = formattedDate.replaceAll(/ /g, "-");
  return format;
};

export const deletePdfPage = async (pdfArrayBuffer, pageNumber) => {
  try {
    // Load the existing PDF
    const pdfDoc = await PDFDocument.load(pdfArrayBuffer);
    // Get the total number of pages
    const totalPages = pdfDoc.getPageCount();
    // Ensure the page index is valid
    if (totalPages > 1) {
      //Remove the specified page
      pdfDoc.removePage(pageNumber - 1);
      // Save the modified PDF
      const modifiedPdfBytes = await pdfDoc.saveAsBase64({
        useObjectStreams: false
      });
      const arrayBuffer = base64ToArrayBuffer(modifiedPdfBytes);
      return {
        arrayBuffer: arrayBuffer,
        base64: modifiedPdfBytes,
        remainingPages: totalPages - 1
      };
    } else {
      return { totalPages: 1 };
    }
  } catch (err) {
    console.log("Err while deleting page", err);
  }
};

export const reorderPdfPages = async (pdfArrayBuffer, orderArr) => {
  try {
    const pdfDoc = await PDFDocument.load(pdfArrayBuffer);
    const newPdf = await PDFDocument.create();
    const pages = await newPdf.copyPages(
      pdfDoc,
      orderArr.map((n) => n - 1)
    );
    pages.forEach((p) => newPdf.addPage(p));
    const pdfBase64 = await newPdf.saveAsBase64({ useObjectStreams: false });
    const arrayBuffer = base64ToArrayBuffer(pdfBase64);
    return { arrayBuffer, base64: pdfBase64, totalPages: orderArr.length };
  } catch (err) {
    console.log("Err while reordering pages", err);
  }
};

// `generatePdfName` is used to generate file name
export function generatePdfName(length) {
  const characters =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  let result = "";
  const charactersLength = characters.length;

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

// Format date and time for the selected timezone
export const formatTimeInTimezone = (date, timezone) => {
  const nyDate = timezone && toZonedTime(date, timezone);
  const generatedDate = timezone
    ? format(nyDate, "EEE, dd MMM yyyy HH:mm:ss zzz", { timeZone: timezone })
    : new Date(date).toUTCString();
  return generatedDate;
};

// `usertimezone` is used to get timezone of current user
export const usertimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
export const handleHeighlightWidget = (
  getCurrentSignerPos,
  key,
  pageNumber
) => {
  const placeholder = getCurrentSignerPos.placeHolder;
  // Find the highest zIndex value
  const highestZIndex = placeholder
    .flatMap((item) => item.pos.map((position) => position.zIndex))
    .reduce((max, zIndex) => (zIndex > max ? zIndex : max), -Infinity); //-Infinity represents the smallest possible number
  // Update the zIndex of the current signer
  const updateZindex = placeholder.map((data) => {
    if (data.pageNumber === pageNumber) {
      return {
        ...data,
        pos: data.pos.map((position) => {
          if (position.key === key) {
            return { ...position, zIndex: highestZIndex + 1 };
          }
          return position;
        })
      };
    }
    return data;
  });
  return updateZindex;
};
/**
 * FlattenPdf is used to remove existing widgets if present any and flatten pdf.
 * @param {string | Uint8Array | ArrayBuffer} pdfFile - pdf file.
 * @returns {Promise<Uint8Array>} flatPdf - pdf file in unit8arry
 */
export const flattenPdf = async (pdfFile) => {
  const pdfDoc = await PDFDocument.load(pdfFile);
  // Get the form
  const form = pdfDoc.getForm();
  // fetch form fields
  const fields = form.getFields();
  // remove form all existing fields and their widgets
  if (fields && fields?.length > 0) {
    try {
      for (const field of fields) {
        while (field.acroField.getWidgets().length) {
          field.acroField.removeWidget(0);
        }
        form.removeField(field);
      }
    } catch (err) {
      console.log("err while removing field from pdf", err);
    }
  }
  // Updates the field appearances to ensure visual changes are reflected.
  form.updateFieldAppearances();
  // Flattens the form, converting all form fields into non-editable, static content
  form.flatten();
  const flatPdf = await pdfDoc.save({ useObjectStreams: false });
  return flatPdf;
};

export const mailTemplate = (param) => {
  const appName = "OpenSign™";
  const logo = `<div style='padding:10px'><img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' /></div>`;

  const opurl = ` <a href='https://www.opensignlabs.com' target=_blank>here</a>.</p></div></div></body></html>`;
  const subject = `${param.senderName} has requested you to sign "${param.title}"`;
  const body =
    "<html><head><meta http-equiv='Content-Type' content='text/html;charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='background:white;padding-bottom:20px'>" +
    logo +
    `<div style='padding:2px;font-family:system-ui;background-color:${themeColor}'><p style='font-size:20px;font-weight:400;color:white;padding-left:20px'>Digital Signature Request</p></div><div><p style='padding:20px;font-size:14px;margin-bottom:10px'>` +
    param.senderName +
    " has requested you to review and sign <strong>" +
    param.title +
    "</strong>.</p><div style='padding: 5px 0px 5px 25px;display:flex;flex-direction:row;justify-content:space-around'><table><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td><td></td><td style='color:#626363;font-weight:bold'>" +
    param.senderMail +
    "</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td><td></td><td style='color:#626363;font-weight:bold'> " +
    param.organization +
    "</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td></td><td style='color:#626363;font-weight:bold'>" +
    param.localExpireDate +
    "</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Note</td><td></td><td style='color:#626363;font-weight:bold'>" +
    param.note +
    "</td></tr><tr><td></td><td></td></tr></table></div> <div style='margin-left:70px'><a target=_blank href=" +
    param.signingUrl +
    "><button style='padding:12px;background-color:#d46b0f;color:white;border:0px;font-weight:bold;margin-top:30px'>Sign here</button></a></div><div style='display:flex;justify-content:center;margin-top:10px'></div></div></div><div><p> This is an automated email from " +
    appName +
    ". For any queries regarding this email, please contact the sender " +
    param.senderMail +
    " directly. If you think this email is inappropriate or spam, you may file a complaint with " +
    appName +
    opurl;

  return { subject, body };
};

export function formatDateTime(date, dateFormat, timeZone, is12Hour) {
  const zonedDate = toZonedTime(date, timeZone); // Convert date to the given timezone
  const timeFormat = is12Hour ? "hh:mm:ss a" : "HH:mm:ss";
  return dateFormat
    ? format(
        zonedDate,
        `${selectFormat(dateFormat)}, ${timeFormat} 'GMT' XXX`,
        { timeZone }
      )
    : formatTimeInTimezone(date, timeZone);
}

export const updateDateWidgetsRes = (
  documentData,
  signerId,
  journey,
  isRemovePrefill
) => {
  const contactUser = documentData?.Signers?.find(
    (data) => data.objectId === signerId
  );
  const extUser =
    localStorage.getItem("Extand_Class") || JSON.stringify([contactUser]);
  let placeHolders = documentData?.Placeholders;
  if (isRemovePrefill) {
    placeHolders = placeHolders?.filter((x) => x.Role !== "prefill");
  }
  const userDetails = extUser ? JSON.parse(extUser)[0] : contactUser;
  return placeHolders?.map((item) => {
    if (item?.signerObjId === signerId || item?.Id === signerId) {
      // Sort page number placeholders
      const sortedPlaceHolder = [...item.placeHolder]
        .sort((a, b) => a.pageNumber - b.pageNumber)
        .map((ph) => ({
          ...ph,
          // Sort positions within each page
          pos: [...ph.pos]
            .sort((a, b) => a.yPosition - b.yPosition)
            .map((widget) => {
              // Update widget values if needed
              if (
                ["name", "email", "job title", "company"].includes(
                  widget.type
                ) &&
                !widget.options.defaultValue &&
                !widget.options.response &&
                userDetails
              ) {
                return {
                  ...widget,
                  options: {
                    ...widget.options,
                    response: widgetDataValue(widget.type, userDetails)
                  }
                };
              }
              return widget;
            })
        }));

      return { ...item, placeHolder: sortedPlaceHolder };
    }

    return item;
  });
};

//function for show checked checkbox
export const selectCheckbox = (ind, selectedCheckbox) => {
  if (selectedCheckbox && selectedCheckbox?.length > 0) {
    const isCheck = selectedCheckbox?.some((data) => data === ind);
    return isCheck || false;
  }
};
export const checkRegularExpress = (validateType, setValidatePlaceholder) => {
  switch (validateType) {
    case "email":
      setValidatePlaceholder("demo@gmail.com");
      break;
    case "number":
      setValidatePlaceholder("12345");
      break;
    case "text":
      setValidatePlaceholder("please enter text");
      break;
    case "ssn":
      setValidatePlaceholder("123-45-6789");
      break;
    default:
      setValidatePlaceholder("please enter value");
  }
};
//function to use unlink signer from widgets
export const handleUnlinkSigner = (
  signerPos,
  setSignerPos,
  signersdata,
  setSignersData,
  uniqueId
) => {
  //remove existing signer's details from 'signerPos' array
  const updatePlaceHolder = signerPos.map((x) => {
    if (x.Id === uniqueId) {
      return { ...x, signerPtr: {}, signerObjId: "" };
    }
    return { ...x };
  });
  setSignerPos(updatePlaceHolder);
  //remove existing signer's details from 'signersdata' array and keep role and id
  const updateSigner = signersdata.map((item) => {
    if (item.Id == uniqueId) {
      return { Role: item.Role, Id: item.Id, blockColor: item.blockColor };
    }
    return item;
  });
  setSignersData(updateSigner);
};
//function is used to get pdf original width and height
export const getOriginalWH = async (pdf) => {
  let pdfWHObj = [];
  //get total page number
  const totalPages = pdf?.numPages;
  //according to page number get all pdf's pages width and height
  for (let index = 0; index < totalPages; index++) {
    try {
      const getPage = await pdf.getPage(index + 1);
      const scale = 1;
      //getting extra height which is removed from viewPort height (letter type pdf have originial h-799.92 but using getPage.getViewport getting height-792 something diffrence-7.92px)
      //getPage?.view[1] store diffrence of height (Page starts 7.92 from bottom)
      const y0 = getPage?.view[1] || 0;
      let { width, height } = getPage.getViewport({ scale });
      //adding it on viewPort height then get original height ex- 792+7.92 = 799.92 orignial height
      height = height + y0;
      pdfWHObj.push({ pageNumber: index + 1, width, height });
    } catch (e) {
      console.log(`Error getting page ${index + 1} of PDF: ${e.message}`);
    }
  }
  return pdfWHObj;
};

//function is used to check required and optional widgets and ensure required widget should be response
export const handleCheckResponse = (checkUser, setminRequiredCount) => {
  let checkboxExist,
    showAlert = false,
    widgetKey,
    requiredCheckbox,
    tourPageNumber; // `pageNumber` is used to check on which page user did not fill widget's data then change current pageNumber and show tour message on that page
  for (let i = 0; i < checkUser[0].placeHolder.length; i++) {
    for (let j = 0; j < checkUser[0].placeHolder[i].pos.length; j++) {
      //get current page
      const updatePage = checkUser[0].placeHolder[i]?.pageNumber;
      //checking checbox type widget
      checkboxExist = checkUser[0].placeHolder[i].pos[j].type === "checkbox";
      //condition to check checkbox widget exist or not
      if (checkboxExist) {
        //get all required type checkbox
        requiredCheckbox = checkUser[0].placeHolder[i].pos.filter(
          (position) =>
            !position.options?.isReadOnly && position.type === "checkbox"
        );
        //if required type checkbox data exit then check user checked all checkbox or some checkbox remain to check
        //also validate to minimum and maximum required checkbox
        if (requiredCheckbox && requiredCheckbox.length > 0) {
          for (let i = 0; i < requiredCheckbox.length; i++) {
            //get minimum required count if  exit
            const minCount =
              requiredCheckbox[i].options?.validation?.minRequiredCount;
            const parseMin = minCount && parseInt(minCount);
            //get maximum required count if  exit
            const maxCount =
              requiredCheckbox[i].options?.validation?.maxRequiredCount;
            const parseMax = maxCount && parseInt(maxCount);
            //in `response` variable is used to get how many checkbox checked by user
            const response = requiredCheckbox[i].options?.response?.length;
            //in `defaultValue` variable is used to get how many checkbox checked by default
            const defaultValue =
              requiredCheckbox[i].options?.defaultValue?.length;
            const checkboxValue = response ? response : defaultValue;
            //condition to check  parseMin  and parseMax greater than 0  then consider it as a required check box
            if (
              parseMin > 0 &&
              parseMax > 0 &&
              !response &&
              !defaultValue &&
              !showAlert
            ) {
              showAlert = true;
              widgetKey = requiredCheckbox[i].key;
              tourPageNumber = updatePage;
              setminRequiredCount(parseMin);
            }
            //else condition to validate minimum required checkbox
            else if (
              parseMin > 0 &&
              (parseMin > checkboxValue || !checkboxValue)
            ) {
              if (!showAlert) {
                showAlert = true;
                widgetKey = requiredCheckbox[i].key;
                tourPageNumber = updatePage;
                setminRequiredCount(parseMin);
              }
            }
          }
        }
      }
      //else condition to check all type widget data fill or not except checkbox
      else {
        //get all required type widgets except checkbox and radio
        const requiredWidgets = checkUser[0].placeHolder[i].pos.filter(
          (position) =>
            position.type === "signature" ||
            (position.options?.status === "required" &&
              position.type !== "checkbox")
        );
        if (requiredWidgets && requiredWidgets?.length > 0) {
          let checkSigned;
          for (let i = 0; i < requiredWidgets?.length; i++) {
            checkSigned = requiredWidgets[i]?.options?.response;
            if (!checkSigned) {
              let checkDefaultSigned =
                requiredWidgets[i]?.options?.defaultValue;
              if (!checkDefaultSigned && !showAlert) {
                showAlert = true;
                widgetKey = requiredWidgets[i].key;
                tourPageNumber = updatePage;
                setminRequiredCount(null);
              }
            }
          }
        }
      }
    }
    //when showAlert is true then break the loop and show alert to fill required data in widgets
    if (showAlert) {
      break;
    }
  }
  return {
    tourPageNumber,
    widgetKey,
    showAlert
  };
};

/**
 * decryptPdf
 * @param {File} file - The password-protected PDF file to decrypt.
 * @param {string | undefined} password - The password used to unlock the PDF.
 * @returns {Promise<File>} - A Promise that resolves to a decrypted PDF as a File object.
 *
 */
export const decryptPdf = async (file, password) => {
  const name = generatePdfName(16);
  const baseApi = localStorage.getItem("baseUrl") || "";
  const url = removeTrailingSegment(baseApi) + "/decryptpdf?ts=" + Date.now();
  let formData = new FormData();
  formData.append("file", file);
  formData.append("password", password);
  const config = {
    headers: { "content-type": "multipart/form-data" },
    responseType: "blob"
  };
  const response = await axios.post(url, formData, config);
  const pdfBlob = new Blob([response.data], { type: "application/pdf" });
  return new File([pdfBlob], name, { type: "application/pdf" });
};

/**
 * Convert a Base64 string to a File object.
 *
 * @param {string} base64String - The Base64 string, with or without the data URI prefix.
 *                                e.g. "data:image/png;base64,iVBORw0KGgoAAAANS…" or "iVBORw0KGgoAAAANS…"
 * @param {string} filename     - Desired filename for the File object, e.g. "photo.png"
 * @returns {File}              - The resulting File object
 */
export function base64ToFile(base64String, filename) {
  // Separate out the mime-type and the actual Base64 payload
  const [header, payload] = base64String.includes(",")
    ? base64String.split(",")
    : [null, base64String];
  // Determine the MIME type (fallback to application/octet-stream)
  const mimeMatch = header?.match(/data:(.*?);base64/);
  const mime = mimeMatch ? mimeMatch[1] : "application/octet-stream";

  // Decode Base64 to raw binary data held in a string
  const binaryString = atob(payload);
  // Create an ArrayBuffer and a view (as unsigned 8-bit)
  const len = binaryString.length;
  const u8arr = new Uint8Array(len);
  for (let i = 0; i < len; i++) {
    u8arr[i] = binaryString.charCodeAt(i);
  }

  // Create a File object (Blob subclass) with the binary data
  return new File([u8arr], filename, { type: mime });
}

/**
 * Reads the given File object and returns its contents as an ArrayBuffer.
 *
 * @param {File} file
 *   The File instance to be read.
 * @returns {Promise<ArrayBuffer>}
 *   A promise that resolves with the file’s binary data as an ArrayBuffer,
 *   or rejects with an error if the read fails.
 */
export function getFileAsArrayBuffer(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = (e) => resolve(e.target.result);
    reader.onerror = (e) => reject(e.target.error);
    reader.readAsArrayBuffer(file);
  });
}

export const sendEmailToSigners = async (
  pdfDetails,
  signersdata,
  customizeMail,
  defaultMail,
  isCustomize
) => {
  let htmlReqBody;
  const owner = pdfDetails?.[0]?.ExtUserPtr;
  let sendMail;
  const getDocumentExpDate = pdfDetails?.[0]?.ExpiryDate?.iso;
  const getTemplateExpDate = new Date(pdfDetails[0]?.createdAt);
  getTemplateExpDate.setDate(
    getTemplateExpDate.getDate() + (pdfDetails[0]?.TimeToCompleteDays || 15)
  );
  const expireDate = getDocumentExpDate
    ? getDocumentExpDate
    : getTemplateExpDate;
  const newDate = new Date(expireDate);
  const localExpireDate = newDate.toLocaleDateString("en-US", {
    day: "numeric",
    month: "long",
    year: "numeric"
  });

  let senderEmail = pdfDetails?.[0]?.ExtUserPtr?.Email;
  let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone;
  let signerMail = signersdata.slice();
  if (pdfDetails?.[0]?.SendinOrder && pdfDetails?.[0]?.SendinOrder === true) {
    signerMail.splice(1);
  }
  for (let i = 0; i < signerMail.length; i++) {
    try {
      let url = `${localStorage.getItem("baseUrl")}functions/sendmailv3`;
      const headers = {
        "Content-Type": "application/json",
        "X-Parse-Application-Id": localStorage.getItem("parseAppId"),
        sessionToken: localStorage.getItem("accesstoken")
      };
      const objectId = signerMail[i].objectId;
      const hostUrl = window.location.origin;
      //encode this url value `${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}` to base64 using `btoa` function
      const encodeBase64 = btoa(
        `${pdfDetails[0]?.objectId}/${signerMail[i].Email}/${objectId}`
      );
      let signPdf = `${hostUrl}/login/${encodeBase64}`;
      const orgName = pdfDetails[0]?.ExtUserPtr.Company
        ? pdfDetails[0].ExtUserPtr.Company
        : "";
      const senderName = pdfDetails?.[0]?.ExtUserPtr.Name;
      const documentName = `${pdfDetails?.[0].Name}`;
      let replaceVar;

      if (customizeMail && isCustomize) {
        const replacedRequestBody = customizeMail?.body.replace(/"/g, "'");
        htmlReqBody =
          "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" +
          replacedRequestBody +
          "</body> </html>";

        const variables = {
          document_title: documentName,
          note: pdfDetails?.[0]?.Note,
          sender_name: senderName,
          sender_mail: senderEmail,
          sender_phone: senderPhone || "",
          receiver_name: signerMail[i]?.Name || "",
          receiver_email: signerMail[i].Email,
          receiver_phone: signerMail[i]?.Phone || "",
          expiry_date: localExpireDate,
          company_name: orgName,
          signing_url: signPdf
        };
        replaceVar = replaceMailVaribles(
          customizeMail.subject,
          htmlReqBody,
          variables
        );
      } else if (defaultMail?.body && defaultMail?.subject) {
        const mailBody = defaultMail?.body;
        const mailSubject = defaultMail.subject;
        const replacedRequestBody = mailBody.replace(/"/g, "'");
        const htmlReqBody =
          "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" +
          replacedRequestBody +
          "</body> </html>";
        const variables = {
          document_title: documentName,
          note: pdfDetails?.[0]?.Note,
          sender_name: senderName,
          sender_mail: senderEmail,
          sender_phone: senderPhone || "",
          receiver_name: signerMail[i]?.Name || "",
          receiver_email: signerMail[i].Email,
          receiver_phone: signerMail[i]?.Phone || "",
          expiry_date: localExpireDate,
          company_name: orgName,
          signing_url: signPdf
        };
        replaceVar = replaceMailVaribles(mailSubject, htmlReqBody, variables);
      }
      const mailparam = {
        senderName: senderName,
        note: pdfDetails?.[0]?.Note || "",
        senderMail: senderEmail,
        title: documentName,
        organization: orgName,
        localExpireDate: localExpireDate,
        signingUrl: signPdf
      };
      let params = {
        extUserId: owner?.objectId,
        recipient: signerMail[i].Email,
        subject: replaceVar?.subject
          ? replaceVar?.subject
          : mailTemplate(mailparam).subject,
        replyto: senderEmail,
        from: senderEmail,
        html: replaceVar?.body ? replaceVar?.body : mailTemplate(mailparam).body
      };

      sendMail = await axios.post(url, params, { headers: headers });
    } catch (error) {
      console.log("error", error);
    }
  }
  if (sendMail?.data?.result?.status === "success") {
    const sessiontoken = localStorage.getItem("accesstoken");
    if (pdfDetails[0]?.objectId && sessiontoken) {
      try {
        let data;
        if (customizeMail && isCustomize) {
          data = {
            RequestBody: customizeMail?.body,
            RequestSubject: customizeMail.subject,
            SendMail: true
          };
        } else if (defaultMail?.body && defaultMail?.subject) {
          data = {
            RequestBody: defaultMail?.body,
            RequestSubject: defaultMail?.subject,
            SendMail: true
          };
        } else {
          data = { SendMail: true };
        }
        try {
          await axios.put(
            `${localStorage.getItem("baseUrl")}classes/contracts_Document/${
              pdfDetails[0]?.objectId
            }`,
            data,
            {
              headers: {
                "Content-Type": "application/json",
                "X-Parse-Application-Id": localStorage.getItem("parseAppId"),
                "X-Parse-Session-Token": sessiontoken
              }
            }
          );
        } catch (err) {
          console.log("axois err ", err);
        }
      } catch (e) {
        console.log("error", e);
      }
    }
    return { status: "success" };
  } else {
    return { status: sendMail?.data?.result?.status };
  }
};
/**
 * Converts a JPEG/JPG File/Blob into a PNG File.
 *
 * @param {string} base64Image  - A data-URL (e.g. "data:image/jpeg;base64,…")
 * @param {string} filename     - Desired filename (e.g. "input").
 * @returns {Promise<File>}     - A promise that resolves with a new PNG File.
 */
export function convertJpegToPng(base64Image, filename) {
  const arr = base64Image.split(",");
  const mimeMatch = arr[0].match(/:(.*?);/);
  if (!mimeMatch) throw new Error("Invalid dataURL");
  const mime = mimeMatch[1];
  const type = mime.split("/")[1];
  if (type === "png") {
    return base64Image;
  } else {
    const bstr = atob(arr[1]);
    let n = bstr.length;
    const u8arr = new Uint8Array(n);
    while (n--) u8arr[n] = bstr.charCodeAt(n);
    const inputFile = new File([u8arr], `${filename}.${type}`, { type: mime });
    return new Promise((resolve, reject) => {
      // ensure it’s JPEG/JPG (you can remove this check if not needed)
      if (!/image\/jpe?g/.test(inputFile.type)) {
        return reject(new Error("Input must be a JPEG or JPG image"));
      }

      // Read the file as a data URL
      const reader = new FileReader();
      reader.onerror = () => reject(new Error("Failed to read the file"));
      reader.onload = () => {
        const img = new Image();
        img.onerror = () => reject(new Error("Failed to load image"));
        img.onload = () => {
          // draw image onto a canvas
          const canvas = document.createElement("canvas");
          canvas.width = img.width;
          canvas.height = img.height;
          const ctx = canvas.getContext("2d");
          ctx.drawImage(img, 0, 0);

          // directly get PNG as base64 data URL
          try {
            const pngDataUrl = canvas.toDataURL("image/png");
            resolve(pngDataUrl);
          } catch (err) {
            reject(new Error("Failed to convert canvas to PNG"));
          }
        };
        img.src = reader.result;
      };
      reader.readAsDataURL(inputFile);
    });
  }
}
//function is used to get assigned signer's email
export const getSignerEmail = (data, signers) => {
  const getEmail =
    signers?.length > 0 &&
    signers.find((x) => x.objectId === data.signerObjId)?.Email;
  return getEmail;
};

//function is used to delete widgets
export const handleDeleteWidget = (key, Id, pageNumber, signerPos) => {
  const updateData = [];

  const filterSignerPos = signerPos.filter((data) => data.Id === Id);

  if (filterSignerPos.length > 0) {
    const getPlaceHolder = filterSignerPos[0].placeHolder;

    const getPageNumer = getPlaceHolder.filter(
      (data) => data.pageNumber === pageNumber
    );

    if (getPageNumer.length > 0) {
      const getXYdata = getPageNumer[0].pos.filter((data) => data.key !== key);

      if (getXYdata.length > 0) {
        updateData.push(getXYdata);
        const newUpdatePos = getPlaceHolder.map((obj) => {
          if (obj.pageNumber === pageNumber) {
            return { ...obj, pos: updateData[0] };
          }
          return obj;
        });

        const newUpdateSigner = signerPos.map((obj) => {
          if (obj.Id === Id) {
            return { ...obj, placeHolder: newUpdatePos };
          }
          return obj;
        });
        return newUpdateSigner;
      } else {
        const getRemainPage = filterSignerPos[0].placeHolder.filter(
          (data) => data.pageNumber !== pageNumber
        );
        //condition to check placeholder length is greater than 1 do not need to remove whole placeholder
        //array only resove particular widgets
        if (getRemainPage && getRemainPage.length > 0) {
          const newUpdatePos = filterSignerPos.map((obj) => {
            if (obj.Id === Id) {
              return { ...obj, placeHolder: getRemainPage };
            }
            return obj;
          });
          let signerupdate = [];
          signerupdate = signerPos.filter((data) => data.Id !== Id);
          signerupdate.push(newUpdatePos[0]);

          return signerupdate;
        } else {
          const updatedData = signerPos
            .map((item) => {
              if (item.Id === Id && item?.Role === "prefill") {
                return null; // mark this item to remove
              }
              if (item.Id === Id) {
                const updatedItem = { ...item };
                delete updatedItem.placeHolder;
                return updatedItem;
              }
              return item;
            })
            .filter(Boolean); // remove nulls (deleted items)

          return updatedData;
        }
      }
    }
  }
};
//function to convert formatted date to new Date() format
export const getDefaultDate = (dateStr, format) => {
  //get valid date format for moment to convert formatted date to new Date() format
  const formats = changeDateToMomentFormat(format);
  const parsedDate = moment(dateStr, formats);
  let date;
  if (parsedDate.isValid()) {
    date = new Date(parsedDate.toISOString());
    return date;
  } else if (dateStr === "today") {
    date = new Date();
    return date;
  }
};
//function to get default format
export const getDefaultFormat = (dateFormat) => dateFormat || "MM/dd/yyyy";

export function generateId(length) {
  const characters =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  let result = "";
  const charactersLength = characters.length;

  for (let i = 0; i < length; i++) {
    result += characters.charAt(Math.floor(Math.random() * charactersLength));
  }
  return result;
}
//function to handle widget background color
export const handleBackground = (data, isNeedSign, uniqueId) => {
  if (data) {
    if (isNeedSign) {
      if (data?.Id === uniqueId) {
        return data?.blockColor + "b0";
      } else {
        return "#dedddc";
      }
    } else {
      return data?.blockColor + "b0";
    }
  } else {
    return "rgba(203, 233, 237, 0.69)";
  }
};
