function formatParts(date: Date, timeZone?: string): string {
  const parts = new Intl.DateTimeFormat("en-CA", {
    timeZone,
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit",
    second: "2-digit",
    hourCycle: "h23",
  }).formatToParts(date);

  const map = new Map(parts.map((part) => [part.type, part.value]));
  const year = map.get("year") ?? "0000";
  const month = map.get("month") ?? "00";
  const day = map.get("day") ?? "00";
  const hour = map.get("hour") ?? "00";
  const minute = map.get("minute") ?? "00";
  const second = map.get("second") ?? "00";

  return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
}

const NAIVE_SCHEDULE_CLOCK_RE =
  /^(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2}):(\d{2})$/;

function parseScheduleClockToMs(
  clock: string,
  scheduleTimezone: string,
): number | null {
  const trimmed = clock.trim();
  if (/[zZ]|[+-]\d{2}:?\d{2}$/.test(trimmed) || trimmed.includes("T")) {
    const ms = Date.parse(trimmed);
    return Number.isNaN(ms) ? null : ms;
  }
  const match = NAIVE_SCHEDULE_CLOCK_RE.exec(trimmed);
  if (!match) {
    const ms = Date.parse(trimmed);
    return Number.isNaN(ms) ? null : ms;
  }
  const [, y, mo, d, h, mi, s] = match;
  if (scheduleTimezone !== "UTC") {
    const ms = Date.parse(`${y}-${mo}-${d}T${h}:${mi}:${s}`);
    return Number.isNaN(ms) ? null : ms;
  }
  return Date.UTC(
    Number(y),
    Number(mo) - 1,
    Number(d),
    Number(h),
    Number(mi),
    Number(s),
  );
}

/** 将时刻格式化为指定 IANA 时区下的 `YYYY-MM-DD`（用于排期日筛选）。 */
export function formatYmdInTimeZone(date: Date, timeZone: string): string {
  try {
    const parts = new Intl.DateTimeFormat("en-CA", {
      timeZone,
      year: "numeric",
      month: "2-digit",
      day: "2-digit",
    }).formatToParts(date);
    const map = new Map(parts.map((part) => [part.type, part.value]));
    const year = map.get("year") ?? "0000";
    const month = map.get("month") ?? "00";
    const day = map.get("day") ?? "00";
    return `${year}-${month}-${day}`;
  } catch {
    const y = date.getFullYear();
    const m = String(date.getMonth() + 1).padStart(2, "0");
    const d = String(date.getDate()).padStart(2, "0");
    return `${y}-${m}-${d}`;
  }
}

/** 排期时区下的「今天」`YYYY-MM-DD`。 */
export function scheduleTodayYmd(timeZone: string): string {
  return formatYmdInTimeZone(new Date(), timeZone);
}

/**
 * 日期选择器单元格对应的排期日字面量（Y-M-D 与格子上显示的数字一致）。
 * 与 API `start_date`/`end_date`（按排期时区解释）对齐。
 */
export function formatSchedulePickerYmd(date: Date): string {
  const y = date.getFullYear();
  const m = String(date.getMonth() + 1).padStart(2, "0");
  const d = String(date.getDate()).padStart(2, "0");
  return `${y}-${m}-${d}`;
}

export function parseSchedulePickerYmd(value: string): Date | undefined {
  const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
  if (!m) return undefined;
  const d = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
  return Number.isNaN(d.getTime()) ? undefined : d;
}

/** IANA 时区短标签（如 UTC、GMT+8）。 */
export function getTimeZoneShortLabel(timeZone: string, date = new Date()): string {
  try {
    const parts = new Intl.DateTimeFormat(undefined, {
      timeZone,
      timeZoneName: "short",
    }).formatToParts(date);
    return parts.find((part) => part.type === "timeZoneName")?.value ?? timeZone;
  } catch {
    return timeZone;
  }
}

/** 浏览器本地时区短标签（如 CST、GMT+8），用于界面说明。 */
export function getBrowserTimeZoneLabel(date = new Date()): string {
  try {
    const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
    const parts = new Intl.DateTimeFormat(undefined, {
      timeZone,
      timeZoneName: "short",
    }).formatToParts(date);
    return parts.find((part) => part.type === "timeZoneName")?.value ?? timeZone;
  } catch {
    return "Local";
  }
}

/**
 * 将接口 ISO 8601 时间格式化为浏览器 **本地时区** 下的 `YYYY-MM-DD HH:mm:ss`。
 *
 * 玩家端订单、钱包、通知、开奖结果等“已发生事件时间”统一走这里。
 * 避免对原始字符串仅 `slice(0,19)`（易把 UTC 刻度误当本地钟面）。
 */
export function formatPlayerInstant(iso: string | null | undefined): string {
  if (iso == null || iso === "") {
    return "—";
  }
  const ms = Date.parse(iso);
  if (Number.isNaN(ms)) {
    return "—";
  }
  return formatParts(new Date(ms));
}

/** @deprecated 玩家端新代码请使用 {@link formatPlayerInstant}。 */
export const formatLotteryInstant = formatPlayerInstant;

/**
 * 将服务端计划时刻（无时区的 `YYYY-MM-DD HH:mm:ss`，按 schedule_timezone 解释，默认 UTC）
 * 格式化为浏览器本地时区的 `YYYY-MM-DD HH:mm:ss`。
 */
export function formatLotteryScheduleClock(
  clock: string | null | undefined,
  scheduleTimezone = "UTC",
): string {
  if (clock == null || clock === "") {
    return "—";
  }
  const ms = parseScheduleClockToMs(clock, scheduleTimezone);
  if (ms == null) {
    return "—";
  }
  return formatParts(new Date(ms));
}

/**
 * 按指定 IANA 时区格式化（管理端等场景）；玩家端展示请用 {@link formatPlayerInstant}。
 */
export function formatLotteryInstantInTimeZone(
  iso: string | null | undefined,
  timeZone: string,
): string {
  if (iso == null || iso === "") {
    return "—";
  }
  const ms = Date.parse(iso);
  if (Number.isNaN(ms)) {
    return "—";
  }
  try {
    return formatParts(new Date(ms), timeZone);
  } catch {
    return formatParts(new Date(ms));
  }
}
