import JSZip from "jszip";
import moment from "moment";

export function classNames(...classes) {
  return classes.filter(Boolean).join(" ");
}

export const getNonNullValue = (value) => {
  if (value !== "") {
    return value;
  } else {
    return undefined;
  }
};

export function filterEmptyFields(object) {
  Object.keys(object).forEach((key) => {
    if (empty(object[key])) {
      delete object[key];
    }
  });
  return object;
}

export function empty(value) {
  return (
    value === "" ||
    value === null ||
    value === undefined ||
    value === "undefined"
  );
}

export const isImage = (file) => {
  const validImageTypes = ["image/gif", "image/jpeg", "image/jpg", "image/png"];
  if (validImageTypes.includes(file.file.type)) return true;
  return false;
};

export const isVideo = (file) => {
  const validVideoTypes = ["video/webm", "video/mp4"];
  if (validVideoTypes.includes(file.file.type)) return true;
  return false;
};

export const isPdf = (file) => {
  const validFileTypes = ["application/pdf"];
  if (validFileTypes.includes(file.file.type)) return true;
  return false;
};

export const isMp3 = (file) => {
  const validFileTypes = ["audio/mpeg"];
  if (validFileTypes.includes(file.file.type)) return true;
  return false;
};

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

export const slugify = (str) =>
  str
    .toLowerCase()
    .trim()
    .replace(/[^ws-]/g, "")
    .replace(/[s_-]+/g, "-")
    .replace(/^-+|-+$/g, "");

/**
 * @typedef {Object} StringCaserOptions
 * @property {"space" | String} separator - define what separates each word, undefined returns no separation - passing "space" separates the words by a space
 * @property {"uppercase" | "lowercase" | "capitalize" | "camelCase" | "PascalCase"} casetype - text case type, uppercase, lowercase of capitalized | default is lowercase
 */
/**
 *
 * @param {String} string - text to convert
 * @param {StringCaserOptions} options - options
 * @returns
 */
export const StringCaser = (string, options) => {
  if (!string) return null;
  if (typeof string !== "string") return null;
  const removedSpecialCharacters = string.replace(/[^a-zA-Z0-9]/g, " ");
  let casedText = [];
  const splitWords = removedSpecialCharacters.split(" ").filter(Boolean);

  if (options?.casetype === "capitalize") {
    casedText = splitWords.map(
      (/** @type {string} */ dt) => `${dt[0].toUpperCase()}${dt.substring(1)} `
    );
  }
  if (options?.casetype === "uppercase") {
    casedText = splitWords.map((/** @type {string} */ dt) => dt.toUpperCase());
  }
  if (options?.casetype === "lowercase") {
    casedText = splitWords.map((/** @type {string} */ dt) => dt.toLowerCase());
  }
  if (options?.casetype === "camelCase") {
    casedText = splitWords.map((/** @type {string} */ dt, index) =>
      index === 0
        ? dt.toLowerCase()
        : `${dt[0].toUpperCase()}${dt.substring(1)} `
    );
  }
  if (options?.casetype === "PascalCase") {
    casedText = splitWords.map(
      (/** @type {string} */ dt) => `${dt[0].toUpperCase()}${dt.substring(1)}`
    );
  }

  if (options?.separator) {
    if (options?.separator === "space") {
      return casedText.join(" ");
    } else {
      return casedText.join(options?.separator);
    }
  } else {
    return casedText.join("");
  }
};

export const generateUUID = () => {
  const s4 = () => {
    return Math.floor((1 + Math.random()) * 0x10000)
      .toString(16)
      .substring(1);
  };

  return (
    s4() +
    s4() +
    "-" +
    s4() +
    "-" +
    s4() +
    "-" +
    s4() +
    "-" +
    s4() +
    s4() +
    s4()
  );
};

export const capitalize = (string) => {
  const removedSpecialCharacters = string.replace(/[^a-zA-Z0-9]/g, " ");

  const splitWords = removedSpecialCharacters.split(" ");
  const capitalized = splitWords.map(
    (dt) => `${dt[0].toUpperCase()}${dt.substring(1)}`
  );

  return capitalized.join(" ");
};

export const dateHandle = (date) => {
  const newDate = date
    ? new Date(date).toISOString().split("T")[0]
    : new Date().toISOString().split("T")[0];
  return newDate;
};

export const graphDate = (date) => {
  const newDate = new Date(date);
  var mS = [
    "Jan",
    "Feb",
    "Mar",
    "Apr",
    "May",
    "June",
    "July",
    "Aug",
    "Sept",
    "Oct",
    "Nov",
    "Dec",
  ];
  return `${newDate.getDate()} ${mS[newDate.getMonth()]}`;
};

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

    return v.toString(16);
  });
};

export const validateUuidv4 = (uuid) => {
  const regex =
    /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89a-b][0-9a-f]{3}-[0-9a-f]{12}$/i;
  return regex.test(uuid);
};

export const getSeasons = () => {
  const currentYear = new Date().getFullYear() - 1;
  const nextYear = currentYear + 1;
  let seasons = [];
  for (let i = 0; i < 10; i++) {
    seasons.push(`${currentYear + i}-${nextYear + i}`);
  }
  return seasons;
};

// seasons = [{ id: 1, name: '2025-2026' }, { id: 2, name: '2023-2024' }]
export const sortSeasonAsc = (seasons) => {
  return seasons.sort((a, b) => {
    const aYear = a.name.split("-")[0];
    const bYear = b.name.split("-")[0];
    return aYear - bYear;
  });
};

export const sortByStringAsc = (array, key) => {
  return array.sort((a, b) => {
    const aVal = a[key].toLowerCase();
    const bVal = b[key].toLowerCase();
    if (aVal < bVal) {
      return -1;
    }
    if (aVal > bVal) {
      return 1;
    }
    return 0;
  });
};

// format calendar events from google calendar API
// expecting format
// [
//   { title: 'Project 1', date: '2023-06-01' },
//   { title: 'Project 2', date: '2023-06-11' },
// ]
export const formatCalendarEvents = (events) => {
  return events.map((event) => {
    return {
      id: event.id,
      title: event.program_name + "-" + event.team_name,
      start: moment(event.mix_date).format("YYYY-MM-DD"),
      backgroundColor: event.mix_type_color,
    };
  });
};

// Net Total = T - M - E
export const calculateNetTotal = (total, managementDiscount, expenses) => {
  return Number(total) - Number(managementDiscount) - Number(expenses);
};

// total = total - discount
export const calculateManagementDiscount = (settings, total) => {
  let managementDiscount = 0;
  let managementDiscountType = "%";
  if (settings.length > 0) {
    managementDiscount = settings.find(
      (setting) => setting.setting_key === "management_value"
    ).setting_value;
    managementDiscountType = settings.find(
      (setting) => setting.setting_key === "management_value_type"
    ).setting_value;
  }
  let managementDiscountVal = 0;
  let managementDiscountStr = "";
  if (managementDiscountType === "%") {
    managementDiscountStr = managementDiscount + "% from the total";
    managementDiscountVal = (total * managementDiscount) / 100;
  } else {
    managementDiscountStr = "$" + managementDiscount + " from the total";
    managementDiscountVal = managementDiscount;
  }
  return {
    managementDiscountVal,
    managementDiscountStr,
  };
};

export const sortSubProjectsAscByTypeName = (subProjects) => {
  let sortedSubProjects = subProjects.sort((a, b) => {
    const getTypeOrder = (type) => {
      // Assign a higher order to "Voiceover" and a lower order to "Song"
      // then consider Tracking 1, Tracking 2, etc.
      const typeLower = type.toLowerCase();

      let baseOrder = 0;

      // Then check for other types
      if (typeLower.includes("voiceover")) {
        baseOrder = 10000; // First
      } else if (typeLower.includes("song")) {
        baseOrder = 20000; // Second
      }
      // Everything else comes after
      else {
        baseOrder = 40000; // Last
      }

      // Extract the numeric part from the type name
      const typeNumberMatch = type.match(/\d+/);
      const typeNumber = typeNumberMatch ? parseInt(typeNumberMatch[0]) : 0;

      return baseOrder + typeNumber;
    };

    const typeA = a.type_name;
    const typeB = b.type_name;

    const orderA = getTypeOrder(typeA);
    const orderB = getTypeOrder(typeB);

    if (orderA < orderB) {
      return -1;
    }
    if (orderA > orderB) {
      return 1;
    }
    return 0;
  });

  return sortedSubProjects;
};

export const countSubProjectsByType = (subProjects) => {
  let voiceCount = 0;
  let songCount = 0;
  let trackingCount = 0;
  let uploadCount = 0;

  subProjects.forEach((item) => {
    let num = parseInt(item.type_name.match(/\d+/));
    if (isNaN(num)) num = 0; // If there's no number, set to 0

    if (
      item.type_name.includes("Voiceover") &&
      !item.type_name.includes("Upload")
    ) {
      if (num > voiceCount) voiceCount = num;
    } else if (item.type_name.includes("Song")) {
      if (num > songCount) songCount = num;
    } else if (item.type_name.includes("Upload")) {
      console.log(item.id);
      if (num > uploadCount) uploadCount = num;
    } else if (item.type_name.includes("Tracking")) {
      if (num > trackingCount) trackingCount = num;
    }
  });

  return {
    voiceCount,
    songCount,
    trackingCount,
    uploadCount,
  };
};

export const stringFormatToLimitedChar = (string, charLimit) => {
  if (string && string.length > charLimit) {
    return string.substring(0, charLimit) + "...";
  } else {
    return string;
  }
};

export const filterEightCountValues = (settings) => {
  const voiceoverEightCount = settings.find(
    (setting) => setting.setting_key === "voiceover_eight_count"
  ).setting_value;

  const songEightCount = settings.find(
    (setting) => setting.setting_key === "song_eight_count"
  ).setting_value;

  const trackingEightCount = settings.find(
    (setting) => setting.setting_key === "tracking_eight_count"
  ).setting_value;

  return { voiceoverEightCount, songEightCount, trackingEightCount };
};

export const downloadFilesByWebLinks = async (links) => {
  try {
    // links are ['https://equalityrecords.s3.amazonaws.com/026122824584Forever+REF.mp3', 'http://website/filename2.pdf']
    const promises = links.map(async (link) => {
      const response = await fetch(link);
      return await response.blob();
    });
    const blobs = await Promise.all(promises);
    blobs.forEach((blob, index) => {
      const link = document.createElement("a");
      link.href = URL.createObjectURL(blob);
      link.download = links[index].split("/").pop(); // file name from the last part of the link
      link.click();
    });
  } catch (error) {}
};

export const removeKeysWhenValueIsNull = (obj) => {
  Object.keys(obj).forEach((key) => obj[key] == null && delete obj[key]);
  return obj;
};

/**
 * @param {Array} tags - array of tags to be replaced
 * @param {Object} data - object containing the data to be replaced
 * @param {String} htmlString - html string containing the tags to be replaced
 * @returns {String} - html string with replaced tags
 */
export const generateHtmlString = (htmlString, data, tags) => {
  let generatedHtmlString = htmlString;

  tags.forEach((tag) => {
    const tagRegExp = new RegExp(`{{{${tag}}}}`, "g");
    generatedHtmlString = generatedHtmlString.replace(tagRegExp, data[tag]);
  });

  return generatedHtmlString;
};

export const downloadAsZipFilesByWebLinks = async (
  links,
  zipFileName,
  onProgress = null
) => {
  try {
    const zip = new JSZip();
    const totalFiles = links.length;
    let completedFiles = 0;

    // First phase: Downloading files (0-80%)
    const promises = links.map(async (link) => {
      const response = await fetch(link);
      if (!response.ok) {
        throw new Error(`Failed to fetch ${link}`);
      }

      // Get file size for progress calculation
      const contentLength = response.headers.get("content-length");
      const total = parseInt(contentLength, 10);
      let loaded = 0;

      // Create a ReadableStream to track download progress
      const reader = response.body.getReader();
      const chunks = [];

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        chunks.push(value);
        loaded += value.length;

        // Calculate individual file progress (0-80%)
        const fileProgress = (loaded / total) * 100;
        const overallProgress =
          (completedFiles / totalFiles) * 100 + fileProgress / totalFiles;
        onProgress?.(Math.min(Math.round(overallProgress * 0.8), 80)); // Cap at 80%
      }

      const blob = new Blob(chunks);
      const fileName = link.split("/").pop();
      zip.file(fileName, blob);

      completedFiles++;
      return fileName;
    });

    await Promise.all(promises);

    // Second phase: Generating zip (80-100%)
    const zipBlob = await zip.generateAsync({
      type: "blob",
      onUpdate: (metadata) => {
        // Convert zip generation progress (0-100) to overall progress (80-100)
        const zipProgress = 80 + (metadata.percent || 0) * 0.2;
        onProgress?.(Math.round(zipProgress));
      },
    });

    const zipLink = document.createElement("a");
    zipLink.href = URL.createObjectURL(zipBlob);
    zipLink.download = `${zipFileName}.zip`;
    zipLink.click();

    // Cleanup
    URL.revokeObjectURL(zipLink.href);
  } catch (error) {
    console.error("Download error:", error);
    throw error;
  }
};

export const downloadAsZipFilesGroupByFolder = async (array, zipFileName) => {
  try {
    const zip = new JSZip();

    const promises = array.map(async (item) => {
      const folder = zip.folder(item.file_name);

      const urls = item.urls;

      const urlPromises = urls.map(async (url) => {
        const response = await fetch(url.url);
        const blob = await response.blob();
        const fileName = url.url.split("/").pop(); // Get the filename from the link

        // Add the blob (file) to the zip archive
        folder.file(fileName, blob);

        return fileName;
      });

      // Wait for all files to be added to the zip archive
      await Promise.all(urlPromises);

      return item.file_name;
    });

    // Wait for all folders to be added to the zip archive
    await Promise.all(promises);

    // Generate the zip file
    const zipBlob = await zip.generateAsync({ type: "blob" });

    // Create a download link for the zip file
    const zipLink = document.createElement("a");
    zipLink.href = URL.createObjectURL(zipBlob);
    zipLink.download = `${zipFileName}.zip`;
    zipLink.click();
  } catch (error) {
    console.error(error);
  }
};

export const copyLinkToClipboard = (link) => {
  try {
    const textArea = document.createElement("textarea");
    const codeToCopy = link.toString();
    textArea.value = codeToCopy;
    document.body.appendChild(textArea);
    textArea.select();
    document.execCommand("copy");
    document.body.removeChild(textArea);
    return true;
  } catch (error) {
    return false;
  }
};

export const replaceNextLineToBrTag = (string) => {
  return string.replace(/\n/g, "<br>");
};

export const replaceBrTagToNextLine = (string) => {
  return string.replace(/<br>/g, "\n");
};

export const resetSubProjectsChronology = (subProjects) => {
  let voiceOverSubProjects = [];
  let songSubProjects = [];
  if (subProjects && subProjects.length > 0) {
    subProjects.forEach((subProject) => {
      let subProjectType = subProject["type"].replace(/[0-9]/g, "");
      subProjectType = subProjectType.replace(/\s/g, "");
      if (subProjectType === "Voiceover") {
        voiceOverSubProjects.push(subProject);
      }
      if (subProject["is_song"]) {
        songSubProjects.push(subProject);
      }
    });
  }
  if (voiceOverSubProjects.length > 0) {
    voiceOverSubProjects.forEach((subProject, index) => {
      subProject["type"] = `Voiceover ${index + 1}`;
    });
  }
  if (songSubProjects.length > 0) {
    songSubProjects.forEach((subProject, index) => {
      subProject["type"] = `Song ${index + 1}`;
    });
  }
  return [...voiceOverSubProjects, ...songSubProjects];
};

export const dateTimeToFormattedString = (dateTime) => {
  if (!dateTime) return "00/00/0000";
  else {
    let dateTimeStr = moment(dateTime)
      .toISOString()
      .replace(/T/, " ")
      .replace(/\..+/, "");
    return moment(dateTimeStr).format("MM/DD/YYYY");
  }
};

export const dateTimeToFormattedString2 = (dateTime) => {
  if (!dateTime) return "00/00/0000 00:00";
  else {
    let dateTimeStr = moment(dateTime)
      .toISOString()
      .replace(/T/, " ")
      .replace(/\..+/, "");
    return moment(dateTimeStr).format("MM/DD/YYYY hh:mm A");
  }
};
