/**
 * Some utils for manipulating dates
 *
 * **WARNING** experimental
 */

/**
 * Return the date to local ISO date, like YYYY-mm-dd
 *
 * The date is valid for the same timezone as the date's locale
 *
 * Do not take time into account
 *
 */
export const dateToISO = (date: Date | null): string | null => {
  if (null === date) {
    return null;
  }

  return [
    date.getFullYear(),
    (date.getMonth() + 1).toString().padStart(2, "0"),
    date.getDate().toString().padStart(2, "0"),
  ].join("-");
};

/**
 * Return a date object from iso string formatted as YYYY-mm-dd
 *
 * **Experimental**
 */
export const ISOToDate = (str: string | null): Date | null => {
  if (null === str) {
    return null;
  }
  if ("" === str.trim()) {
    return null;
  }

  // If the string already contains time info, use it directly
  if (str.includes("T") || str.includes(" ")) {
    return new Date(str);
  }

  // Otherwise, parse date only
  const [year, month, day] = str.split("-").map((p) => parseInt(p));
  return new Date(year, month - 1, day, 0, 0, 0, 0);
};

/**
 * Return a date object from iso string formatted as YYYY-mm-dd:HH:MM:ss+01:00
 *
 */
export const ISOToDatetime = (str: string | null): Date | null => {
  if (null === str) {
    return null;
  }

  const [cal, times] = str.split("T"),
    [year, month, date] = cal.split("-").map((s) => parseInt(s)),
    [time, timezone] = times.split(times.charAt(8)),
    [hours, minutes, seconds] = time.split(":").map((s) => parseInt(s));
  if ("0000" === timezone) {
    return new Date(Date.UTC(year, month - 1, date, hours, minutes, seconds));
  }

  return new Date(year, month - 1, date, hours, minutes, seconds);
};

/**
 * Convert a date to ISO8601, valid for usage in api
 *
 */
export const datetimeToISO = (date: Date): string => {
  const cal = [
    date.getFullYear(),
    (date.getMonth() + 1).toString().padStart(2, "0"),
    date.getDate().toString().padStart(2, "0"),
  ].join("-");

  const time = [
    date.getHours().toString().padStart(2, "0"),
    date.getMinutes().toString().padStart(2, "0"),
    date.getSeconds().toString().padStart(2, "0"),
  ].join(":");

  const offset = [
    date.getTimezoneOffset() <= 0 ? "+" : "-",
    Math.abs(Math.floor(date.getTimezoneOffset() / 60))
      .toString()
      .padStart(2, "0"),
    ":",
    Math.abs(date.getTimezoneOffset() % 60)
      .toString()
      .padStart(2, "0"),
  ].join("");

  const x = cal + "T" + time + offset;

  return x;
};

export const intervalDaysToISO = (days: number | string | null): string => {
  if (null === days) {
    return "P0D";
  }

  return `P${days}D`;
};

export const intervalISOToDays = (str: string | null): number | null => {
  if (null === str) {
    return null;
  }

  if ("" === str.trim()) {
    return null;
  }

  let days = 0;
  let isDate = true;
  let vstring = "";
  for (let i = 0; i < str.length; i = i + 1) {
    if (!isDate) {
      continue;
    }
    switch (str.charAt(i)) {
      case "P":
        isDate = true;
        break;
      case "T":
        isDate = false;
        break;
      case "0":
      case "1":
      case "2":
      case "3":
      case "4":
      case "5":
      case "6":
      case "7":
      case "8":
      case "9":
        vstring = vstring + str.charAt(i);
        break;
      case "Y":
        days = days + Number.parseInt(vstring) * 365;
        vstring = "";
        break;
      case "M":
        days = days + Number.parseInt(vstring) * 30;
        vstring = "";
        break;
      case "D":
        days = days + Number.parseInt(vstring);
        vstring = "";
        break;
      default:
        throw Error("this character should not appears: " + str.charAt(i));
    }
  }

  return days;
};

export function getTimezoneOffsetString(date: Date, timeZone: string): string {
  const utcDate = new Date(date.toLocaleString("en-US", { timeZone: "UTC" }));
  const tzDate = new Date(date.toLocaleString("en-US", { timeZone }));
  const offsetMinutes = (utcDate.getTime() - tzDate.getTime()) / (60 * 1000);

  // Inverser le signe pour avoir la convention ±HH:MM
  const sign = offsetMinutes <= 0 ? "+" : "-";
  const absMinutes = Math.abs(offsetMinutes);
  const hours = String(Math.floor(absMinutes / 60)).padStart(2, "0");
  const minutes = String(absMinutes % 60).padStart(2, "0");

  return `${sign}${hours}:${minutes}`;
}
