// Rôle : regroupe les calculs purs de formatage et d'échéance des timers.
export function getNowMs() {
  return Date.now();
}

export function pad(value) {
  return String(value).padStart(2, "0");
}

export function formatDuration(ms) {
  const safeMs = Math.max(0, Math.floor(ms));
  const totalSeconds = Math.floor(safeMs / 1000);
  const hours = Math.floor(totalSeconds / 3600);
  const minutes = Math.floor((totalSeconds % 3600) / 60);
  const seconds = totalSeconds % 60;
  return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
}

export function getCentiseconds(ms) {
  return pad(Math.floor((Math.max(0, Math.floor(ms)) % 1000) / 10));
}

export function formatDurationWithCentiseconds(ms) {
  return `${formatDuration(ms)}:${getCentiseconds(ms)}`;
}

export function formatTargetTime(ms) {
  if (!Number.isFinite(ms)) return "";
  return new Intl.DateTimeFormat("fr-FR", {
    hour: "2-digit",
    minute: "2-digit",
    second: "2-digit"
  }).format(new Date(ms));
}

export function normalizeTimePart(value, max, allowWildcard = false) {
  const cleanValue = String(value || "").trim().toUpperCase();
  if (allowWildcard && !cleanValue) return "X";
  if (allowWildcard && /^X+$/.test(cleanValue)) return "X";
  const parsed = cleanValue ? Number.parseInt(cleanValue, 10) : 0;
  if (!Number.isFinite(parsed) || parsed < 0 || parsed > max) return "";
  return pad(parsed);
}

export function normalizeTimeParts(parts, { allowWildcard = false, allowLargeHours = false } = {}) {
  const hours = normalizeTimePart(parts?.hours, allowLargeHours ? 999 : 23, allowWildcard);
  const minutes = normalizeTimePart(parts?.minutes, 59, allowWildcard);
  const seconds = normalizeTimePart(parts?.seconds, 59, allowWildcard);
  if (!hours || !minutes || !seconds) return null;
  return { hours, minutes, seconds };
}

export function timePartsToDurationMs(parts) {
  const normalized = normalizeTimeParts(parts, { allowLargeHours: true });
  if (!normalized) return 0;
  return ((Number(normalized.hours) * 3600) + (Number(normalized.minutes) * 60) + Number(normalized.seconds)) * 1000;
}

export function timePartsToString(parts, options) {
  const normalized = normalizeTimeParts(parts, options);
  return normalized ? `${normalized.hours}:${normalized.minutes}:${normalized.seconds}` : "";
}

export function parseTimeString(value, options) {
  const [hours = "", minutes = "", seconds = ""] = String(value || "").split(":");
  return normalizeTimeParts({ hours, minutes, seconds }, options);
}

export function getDailyTargetMs(time, nowMs) {
  const parsed = parseTimeString(time);
  if (!parsed) return 0;
  const target = new Date(nowMs);
  target.setHours(Number(parsed.hours), Number(parsed.minutes), Number(parsed.seconds), 0);
  if (target.getTime() <= nowMs) target.setDate(target.getDate() + 1);
  return target.getTime();
}

export function getTimePatternTargetMs(pattern, nowMs) {
  const parsed = parseTimeString(pattern, { allowWildcard: true });
  if (!parsed) return 0;
  const now = new Date(nowMs);
  const candidate = new Date(nowMs);
  candidate.setMilliseconds(0);
  const hasHour = parsed.hours !== "X";
  const hasMinute = parsed.minutes !== "X";

  for (let offset = 1; offset <= 86400; offset += 1) {
    candidate.setTime(now.getTime() + offset * 1000);
    const hours = parsed.hours === "X" || Number(parsed.hours) === candidate.getHours();
    const minutes = parsed.minutes === "X"
      ? (!hasHour || candidate.getMinutes() === 0)
      : Number(parsed.minutes) === candidate.getMinutes();
    const seconds = parsed.seconds === "X"
      ? (!hasHour && !hasMinute) || candidate.getSeconds() === 0
      : Number(parsed.seconds) === candidate.getSeconds();
    if (hours && minutes && seconds) return candidate.getTime();
  }
  return 0;
}

export function getTimePatternRecurrenceMs(pattern) {
  const baseMs = new Date(2026, 0, 1, 0, 0, 0, 0).getTime();
  const firstTargetMs = getTimePatternTargetMs(pattern, baseMs);
  if (!firstTargetMs) return 0;
  const secondTargetMs = getTimePatternTargetMs(pattern, firstTargetMs);
  return secondTargetMs ? secondTargetMs - firstTargetMs : 0;
}

export function getIntervalTargetMs(intervalMs, anchorAt, nowMs) {
  const safeIntervalMs = Math.max(1000, Number(intervalMs) || 0);
  const safeAnchor = Number.isFinite(anchorAt) && anchorAt > 0 ? anchorAt : nowMs;
  if (safeAnchor > nowMs) return safeAnchor;
  return safeAnchor + Math.ceil((nowMs - safeAnchor + 1) / safeIntervalMs) * safeIntervalMs;
}

export function getIntervalAnchorMs(startMode, startTime, nowMs) {
  if (startMode !== "time") return nowMs;
  const parsed = parseTimeString(startTime);
  if (!parsed) return nowMs;
  const anchor = new Date(nowMs);
  anchor.setHours(Number(parsed.hours), Number(parsed.minutes), Number(parsed.seconds), 0);
  return anchor.getTime();
}

export function getCountdownTargetMs(countdown, nowMs) {
  if (countdown.type === "duration") return Number(countdown.targetAt) || 0;
  if (countdown.type === "daily_time") return countdown.autoRefresh ? getDailyTargetMs(countdown.time, nowMs) : Number(countdown.targetAt) || 0;
  if (countdown.type === "time_pattern") return getTimePatternTargetMs(countdown.pattern, nowMs);
  if (countdown.type === "interval") {
    if (countdown.autoRefresh) return getIntervalTargetMs(countdown.intervalMs, countdown.anchorAt, nowMs);
    return Number(countdown.targetAt) || 0;
  }
  return 0;
}
