const DATE_YMD_RE = /^(\d{4})-(\d{2})-(\d{2})$/;

function pad2(n: number): string {
  return String(n).padStart(2, "0");
}

/** 本地日历 `YYYY-MM-DD` */
export function toDateYmdValue(date: Date): string {
  return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`;
}

function parseDateYmd(value: string): Date | null {
  const match = DATE_YMD_RE.exec(value.trim());
  if (!match) {
    return null;
  }
  const [, y, mo, d] = match;
  const date = new Date(Number(y), Number(mo) - 1, Number(d));
  return Number.isNaN(date.getTime()) ? null : date;
}

function formatUtcDateTimeFromMs(ms: number): string {
  const date = new Date(ms);
  return `${date.getUTCFullYear()}-${pad2(date.getUTCMonth() + 1)}-${pad2(date.getUTCDate())} ${pad2(date.getUTCHours())}:${pad2(date.getUTCMinutes())}:${pad2(date.getUTCSeconds())}`;
}

/** 本地自然日 00:00:00 → UTC `YYYY-MM-DD HH:mm:ss`（开账 API） */
export function localDateYmdToUtcPeriodStart(ymd: string): string {
  const date = parseDateYmd(ymd);
  if (date === null) {
    return ymd;
  }
  const ms = new Date(
    date.getFullYear(),
    date.getMonth(),
    date.getDate(),
    0,
    0,
    0,
    0,
  ).getTime();

  return formatUtcDateTimeFromMs(ms);
}

/** 本地自然日 23:59:59 → UTC `YYYY-MM-DD HH:mm:ss`（开账 API） */
export function localDateYmdToUtcPeriodEnd(ymd: string): string {
  const date = parseDateYmd(ymd);
  if (date === null) {
    return ymd;
  }
  const ms = new Date(
    date.getFullYear(),
    date.getMonth(),
    date.getDate(),
    23,
    59,
    59,
    999,
  ).getTime();

  return formatUtcDateTimeFromMs(ms);
}

export function localDateRangeToUtcPeriodBounds(
  startYmd: string,
  endYmd: string,
): { period_start: string; period_end: string } {
  return {
    period_start: localDateYmdToUtcPeriodStart(startYmd),
    period_end: localDateYmdToUtcPeriodEnd(endYmd),
  };
}

/** UTC 账期时刻 → 本地日历 `YYYY-MM-DD`（列表展示） */
export function utcPeriodInstantToLocalYmd(iso: string | null | undefined): string {
  if (iso == null || iso === "") {
    return "—";
  }
  const ms = Date.parse(iso);
  if (Number.isNaN(ms)) {
    return iso.slice(0, 10);
  }
  return toDateYmdValue(new Date(ms));
}

/** UTC 存储日 `Y-m-d` → 本地日历标记用 `yyyy-MM-dd` */
export function utcStorageDateToLocalMarkYmd(utcYmd: string): string {
  const match = DATE_YMD_RE.exec(utcYmd.trim());
  if (!match) {
    return utcYmd;
  }
  const [, y, mo, d] = match;
  const ms = Date.UTC(Number(y), Number(mo) - 1, Number(d), 12, 0, 0, 0);
  return toDateYmdValue(new Date(ms));
}

export function utcStorageDatesToLocalMarks(dates: string[]): string[] {
  return dates.map((day) => utcStorageDateToLocalMarkYmd(day));
}

/** 开账建议：UTC 存储日 → 本地表单 `yyyy-MM-dd` */
export function utcStorageDateToLocalFormYmd(utcYmd: string): string {
  return utcStorageDateToLocalMarkYmd(utcYmd);
}

export function formatSettlementPeriodSpan(
  periodStart: string | undefined,
  periodEnd: string | undefined,
): string {
  const start = utcPeriodInstantToLocalYmd(periodStart);
  const end = utcPeriodInstantToLocalYmd(periodEnd);

  return `${start} ~ ${end}`;
}

export function isSettlementLocalDateRangeValid(startYmd: string, endYmd: string): boolean {
  const start = parseDateYmd(startYmd);
  const end = parseDateYmd(endYmd);
  if (start === null || end === null) {
    return false;
  }

  return start.getTime() <= end.getTime();
}

function eachLocalYmdInRange(startYmd: string, endYmd: string): string[] {
  const start = parseDateYmd(startYmd);
  const end = parseDateYmd(endYmd);
  if (start === null || end === null || start.getTime() > end.getTime()) {
    return [];
  }

  const days: string[] = [];
  const cursor = new Date(start.getFullYear(), start.getMonth(), start.getDate());
  const endMs = new Date(end.getFullYear(), end.getMonth(), end.getDate()).getTime();

  while (cursor.getTime() <= endMs) {
    days.push(toDateYmdValue(cursor));
    cursor.setDate(cursor.getDate() + 1);
  }

  return days;
}

/** 本地账期范围是否与已有账期日期重叠 */
export function settlementRangeOverlapsOccupiedDates(
  startYmd: string,
  endYmd: string,
  occupiedLocalYmds: string[],
): boolean {
  if (occupiedLocalYmds.length === 0) {
    return false;
  }

  const occupied = new Set(occupiedLocalYmds);
  return eachLocalYmdInRange(startYmd, endYmd).some((day) => occupied.has(day));
}
