import i18n from "@/i18n";

export function getTimeStampByDate(t: Date | number | string | any): number {
  return new Date(t).getTime();
}

export function getDateStampByDate(t?: Date | number | string): number {
  const tsFromDate = getTimeStampByDate(t ? t : Date.now());
  const d = new Date(tsFromDate);

  return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
}

/**
 * Get a time string to provided time.
 *
 * If no date is provided, the current date is used.
 *
 * Output is always ``HH:MM`` (24-hour format)
 */
export function getTimeString(t?: Date | number | string): string {
  const tsFromDate = getTimeStampByDate(t ? t : Date.now());
  const d = new Date(tsFromDate);

  const hours = d.getHours();
  const mins = d.getMinutes();

  const hoursStr = hours < 10 ? "0" + hours : hours;
  const minsStr = mins < 10 ? "0" + mins : mins;

  return `${hoursStr}:${minsStr}`;
}

/**
 * Get a localized date and time string to provided time.
 *
 * If no date is provided, the current date is used.
 *
 * Sample outputs:
 * - "en" locale: "1/30/2023, 10:05:00 PM"
 * - "pt-BR" locale: "30/01/2023 22:05:00"
 * - "pl" locale: "30.01.2023, 22:05:00"
 */
export function getDateTimeString(t?: Date | number | string | any, locale = i18n.language): string {
  const tsFromDate = new Date(getTimeStampByDate(t ? t : Date.now()));
  try {
    return tsFromDate.toLocaleString(locale, {
      year: "numeric",
      month: "numeric",
      day: "numeric",
      hour: "numeric",
      minute: "numeric",
      second: "numeric",
    });
  } catch (error) {
    return tsFromDate.toLocaleString();
  }
}

/**
 * Get a localized relative time string to provided time.
 *
 * Possible outputs for "long" format and "en" locale:
 * - "x seconds ago"
 * - "x minutes ago"
 * - "x hours ago"
 * - "yesterday"
 * - "x days ago"
 * - "x weeks ago"
 * - "x months ago"
 * - "last year"
 * - "x years ago"
 */
export const getRelativeTimeString = (time: number, locale = i18n.language, formatStyle: "long" | "short" | "narrow" = "long"): string => {
  const pastTimeMillis = Date.now() - time;
  const secMillis = 1000;
  const minMillis = secMillis * 60;
  const hourMillis = minMillis * 60;
  const dayMillis = hourMillis * 24;
  // Show full date if more than 1 day ago.
  if (pastTimeMillis >= dayMillis) {
    return getDateTimeString(time, locale);
  }

  // numeric: "auto" provides "yesterday" for 1 day ago, "always" provides "1 day ago"
  const formatOpts = { style: formatStyle, numeric: "auto" } as Intl.RelativeTimeFormatOptions;
  const relTime = new Intl.RelativeTimeFormat(locale, formatOpts);
  if (pastTimeMillis < minMillis) {
    return relTime.format(-Math.round(pastTimeMillis / secMillis), "second");
  }
  if (pastTimeMillis < hourMillis) {
    return relTime.format(-Math.round(pastTimeMillis / minMillis), "minute");
  }
  if (pastTimeMillis < dayMillis) {
    return relTime.format(-Math.round(pastTimeMillis / hourMillis), "hour");
  }
  if (pastTimeMillis < dayMillis * 7) {
    return relTime.format(-Math.round(pastTimeMillis / dayMillis), "day");
  }
  if (pastTimeMillis < dayMillis * 30) {
    return relTime.format(-Math.round(pastTimeMillis / (dayMillis * 7)), "week");
  }
  if (pastTimeMillis < dayMillis * 365) {
    return relTime.format(-Math.round(pastTimeMillis / (dayMillis * 30)), "month");
  }

  return relTime.format(-Math.round(pastTimeMillis / (dayMillis * 365)), "year");
};

/**
 * This returns the normalized date string of the provided date.
 * Format is always `YYYY-MM-DDT00:00`.
 *
 * If no date is provided, the current date is used.
 */
export function getNormalizedTimeString(t?: Date | number | string): string {
  const date = new Date(t ? t : Date.now());

  const yyyy = date.getFullYear();
  const M = date.getMonth() + 1;
  const d = date.getDate();
  const h = date.getHours();
  const m = date.getMinutes();

  const MM = M < 10 ? "0" + M : M;
  const dd = d < 10 ? "0" + d : d;
  const hh = h < 10 ? "0" + h : h;
  const mm = m < 10 ? "0" + m : m;

  return `${yyyy}-${MM}-${dd}T${hh}:${mm}`;
}

export function getNormalizedDateString(t?: Date | number | string): string {
  const date = new Date(t ? t : Date.now());

  const yyyy = date.getFullYear();
  const M = date.getMonth() + 1;
  const d = date.getDate();

  const MM = M < 10 ? "0" + M : M;
  const dd = d < 10 ? "0" + d : d;

  return `${yyyy}-${MM}-${dd}`;
}

/**
 * This returns the Unix timestamp (the number of **seconds** since the Unix Epoch) of the provided date.
 *
 * If no date is provided, the current date is used.
 * ```
 * getUnixTime("2019-01-25 00:00") // 1548381600
 * ```
 * This value is floored to the nearest second, and does not include a milliseconds component.
 */
export function getUnixTime(t?: Date | number | string): number {
  const date = new Date(t ? t : Date.now());
  return Math.floor(date.getTime() / 1000);
}

/**
 * Checks if the provided date or timestamp is in the future.
 *
 * If no date is provided, the current date is used.
 *
 * @param t - Date or timestamp to check.
 * @returns `true` if the date is in the future, `false` otherwise.
 */
export function isFutureDate(t?: Date | number | string): boolean {
  const timestamp = getTimeStampByDate(t ? t : Date.now());
  return timestamp > Date.now();
}

/**
 * Calculates a new Date object by adjusting the provided date, timestamp, or date string
 * based on the current timezone offset.
 *
 * @param t - The input date, timestamp, or date string (optional). If not provided,
 *            the current date and time will be used.
 * @returns A new Date object adjusted by the current timezone offset.
 */
export function getDateWithOffset(t?: Date | number | string): Date {
  return new Date(getTimeStampByDate(t) + new Date().getTimezoneOffset() * 60 * 1000);
}
