// The Lucia project.
// Copyright 2023-2026 DSP, inc.  All rights reserved.
// Authors:
//   chiayin.kuo@dsp.im (chiayin), 2023/1/31
//   imacat.yang@dsp.im (imacat), 2023/9/23
//   cindy.chang@dsp.im (Cindy Chang), 2024/5/30
/** @module timeLabel Time formatting and chart axis tick utilities. */

import moment from "moment";

/** @constant {number} Number of decimal places for formatted time values. */
const TOFIXED_DECIMAL = 1;

/**
 * Calculates the step size and time unit for evenly spaced Y-axis ticks.
 *
 * @param {number} maxTimeInSecond - The maximum time value in seconds.
 * @param {number} numOfParts - The number of equal parts to divide into.
 * @returns {{resultStepSize: number, unitToUse: string}} The step size
 *   and the time unit character ("d", "h", "m", or "s").
 */
export const getStepSizeOfYTicks = (maxTimeInSecond, numOfParts) => {
  const { unitToUse, timeValue } = getTimeUnitAndValueToUse(maxTimeInSecond);
  const getLarger = 1 + 1 / (numOfParts - 1);
  const resultStepSize = (timeValue * getLarger) / numOfParts;

  return { resultStepSize, unitToUse };
};

/**
 * Determines the most appropriate time unit for a given number of seconds
 * and converts the value to that unit.
 *
 * @param {number} secondToDecide - The time in seconds to evaluate.
 * @returns {{unitToUse: string, timeValue: number}} The chosen unit
 *   character ("d", "h", "m", or "s") and the converted time value.
 */
const getTimeUnitAndValueToUse = (secondToDecide) => {
  const day = 24 * 60 * 60;
  const hour = 60 * 60;
  const minutes = 60;
  const dd = secondToDecide / day;
  const hh = secondToDecide / hour;
  const mm = secondToDecide / minutes;

  if (dd > 0 && dd > 1) {
    return {
      unitToUse: "d",
      timeValue: secondToDecide / day,
    };
  } else if (hh > 0 && hh > 1) {
    return {
      unitToUse: "h",
      timeValue: secondToDecide / hour,
    };
  } else if (mm > 0 && mm > 1) {
    return {
      unitToUse: "m",
      timeValue: secondToDecide / minutes,
    };
  } else {
    return {
      unitToUse: "s",
      timeValue: secondToDecide,
    };
  }
};

/**
 * Formats a Y-axis tick value by multiplying the step size by the
 * tick index and appending the time unit suffix.
 *
 * @param {number} stepSize - The step size between Y-axis ticks.
 * @param {number} index - The zero-based index of the current tick.
 * @param {string} unitToUse - The time unit suffix ("d", "h", "m", or "s").
 * @returns {string} The formatted tick label (e.g. "2.5h").
 */
export function getYTicksByIndex(stepSize, index, unitToUse) {
  const shortenStepsizeMultIndex = (stepSize * index).toFixed(TOFIXED_DECIMAL);
  return `${shortenStepsizeMultIndex}${unitToUse}`;
}

/**
 * Converts seconds to a human-readable time string with full unit names.
 *
 * @param {number} second - The total number of seconds.
 * @param {number} [fixedNumber=0] - Number of decimal places.
 * @returns {string} The formatted string (e.g. "1 days", "6.8 hrs",
 *   "30 mins", "5 sec").
 */
export function getTimeLabel(second, fixedNumber = 0) {
  const day = 24 * 60 * 60;
  const hour = 60 * 60;
  const minutes = 60;

  // The modulo operation limits the value to within one day (0 to 86399),
  // representing the remaining seconds after full days.
  const dd = Math.floor(second / day);
  const hh = Math.floor((second % day) / hour);
  const mm = Math.floor((second % hour) / minutes);

  if (dd > 0) {
    return (second / day).toFixed(fixedNumber) + " days";
  } else if (hh > 0) {
    return ((second % day) / hour).toFixed(fixedNumber) + " hrs";
  } else if (mm > 0) {
    return ((second % hour) / minutes).toFixed(fixedNumber) + " mins";
  }
  if (second === 0) {
    return second + " sec";
  }
  return second + " sec";
}

/**
 * Converts seconds to a compact time string with abbreviated unit suffixes.
 *
 * Automatically selects the most appropriate unit: "d" for days,
 * "h" for hours, "m" for minutes, or "s" for seconds.
 *
 * @param {number} second - The total number of seconds.
 * @param {number} [fixedNumber=0] - Number of decimal places.
 * @returns {string} The formatted string (e.g. "1d", "6.8h", "30m", "5s").
 */
export function simpleTimeLabel(second, fixedNumber = 0) {
  const day = 24 * 60 * 60;
  const hour = 60 * 60;
  const minutes = 60;
  const dd = Math.floor(second / day);
  const hh = Math.floor((second % day) / hour);
  const mm = Math.floor((second % hour) / minutes);

  if (dd > 0) {
    return (second / day).toFixed(fixedNumber) + "d";
  } else if (hh > 0) {
    return ((second % day) / hour).toFixed(fixedNumber) + "h";
  } else if (mm > 0) {
    return ((second % hour) / minutes).toFixed(fixedNumber) + "m";
  }
  if (second === 0) {
    return second + "s";
  }
  return second + "s";
}
/**
 * Converts seconds to a time string using the same unit as the maximum
 * value in the dataset, ensuring consistent Y-axis labels.
 *
 * The unit is determined by the max value: days if max > 1 day,
 * hours if max > 1 hour, minutes if max > 1 minute, else seconds.
 *
 * @param {number} second - The time value in seconds to format.
 * @param {number} max - The maximum time value in the dataset (seconds).
 * @param {number} [fixedNumber=0] - Number of decimal places.
 * @returns {string} The formatted string (e.g. "1.5d", "6.8h").
 */
export function followTimeLabel(second, max, fixedNumber = 0) {
  const day = 24 * 60 * 60;
  const hour = 60 * 60;
  const minutes = 60;

  const dd = max / day;
  const hh = max / hour;
  const mm = max / minutes;
  let maxUnit = "";
  let result = "";

  if (dd > 1) {
    maxUnit = "d";
  } else if (hh > 1) {
    maxUnit = "h";
  } else if (mm > 1) {
    maxUnit = "m";
  } else {
    maxUnit = "s";
  }
  switch (maxUnit) {
    case "d":
      if (second / day === 0) {
        fixedNumber = 0;
      }
      result = (second / day).toFixed(fixedNumber) + "d";
      break;
    case "h":
      if (second / hour === 0) {
        fixedNumber = 0;
      }
      result = (second / hour).toFixed(fixedNumber) + "h";
      break;
    case "m":
      if (second / minutes === 0) {
        fixedNumber = 0;
      }
      result = (second / minutes).toFixed(fixedNumber) + "m";
      break;
    case "s":
      if (second === 0) {
        fixedNumber = 0;
      }
      result = second.toFixed(fixedNumber) + "s";
      break;
  }
  return result;
}

/**
 * Selects an appropriate moment.js date format string based on the
 * difference between the minimum and maximum timestamps.
 *
 * Returns "HH:mm:ss" for sub-minute ranges, "MM/DD HH:mm" for
 * sub-day ranges, and "YYYY/MM/DD" for longer ranges.
 *
 * @param {number} minTimeStamp - The minimum timestamp in seconds.
 * @param {number} maxTimeStamp - The maximum timestamp in seconds.
 * @returns {string} A moment.js format string.
 */
export const setTimeStringFormatBaseOnTimeDifference = (
  minTimeStamp,
  maxTimeStamp,
) => {
  const timeDifferenceInSeconds = maxTimeStamp - minTimeStamp;

  let dateFormat;
  if (timeDifferenceInSeconds < 60) {
    dateFormat = "HH:mm:ss"; // Seconds range
  } else if (timeDifferenceInSeconds < 3600) {
    dateFormat = "MM/DD HH:mm"; // Minutes range
  } else if (timeDifferenceInSeconds < 86400) {
    // 86400 seconds = 24 hours
    dateFormat = "MM/DD HH:mm"; // Hours range
  } else if (timeDifferenceInSeconds < 2592000) {
    // 2592000 seconds = 30 days
    dateFormat = "YYYY/MM/DD"; // Days range
  } else {
    dateFormat = "YYYY/MM/DD"; // Months range
  }

  return dateFormat;
};

/**
 * Converts an array of Unix timestamps to formatted date strings for
 * use as chart axis tick labels.
 *
 * @param {Array<number>} timeStampArr - Array of Unix timestamps.
 * @param {string} timeFormat - A moment.js format string (e.g. "MM/DD").
 * @returns {Array<string>|undefined} Array of formatted date strings,
 *   or undefined if timeStampArr is falsy.
 */
export const mapTimestampToAxisTicksByFormat = (timeStampArr, timeFormat) => {
  if (timeStampArr) {
    return timeStampArr.map((ts) => moment(ts).format(timeFormat));
  }
  return [];
};
