// Pure, unit-testable parser for human-typed times in the executive
// meetings inline time editor. The native <input type="time"> only
// surfaces a value to the controlled state when the typed text is a
// complete, canonical "HH:MM" 24h string — partial input, "9:30"
// without a leading zero, "0930" compact, "9:30 PM" / "9:30 ص", or
// Arabic-Indic digits all leave the React state stale, which made
// "type then press Enter" a silent no-op. We replace the native time
// input with a plain text input and route every save path through
// this parser so what the user types is what gets saved.
//
// Returns canonical "HH:MM" (24h, zero-padded) or null when the input
// cannot be parsed. Empty / whitespace-only input returns null too —
// the caller decides whether that means "clear the time" (current
// behaviour) or "show a parse error".

export function parseTypedTime(input: string): string | null {
  if (typeof input !== "string") return null;
  let s = input.trim();
  if (!s) return null;

  // Map Arabic-Indic (٠-٩) and Persian/Eastern Arabic-Indic (۰-۹)
  // digits to ASCII so an Arabic user typing "٠٩:٣٠" parses identically
  // to "09:30".
  s = s.replace(/[\u0660-\u0669]/g, (d) =>
    String(d.charCodeAt(0) - 0x0660),
  );
  s = s.replace(/[\u06F0-\u06F9]/g, (d) =>
    String(d.charCodeAt(0) - 0x06F0),
  );

  // Detect a trailing AM/PM marker. Accepts English (am/pm, a.m./p.m.,
  // any case) and Arabic (ص for morning/AM, م for evening/PM). The
  // marker is stripped before numeric parsing and applied at the end.
  let isAm = false;
  let isPm = false;
  const amPmMatch = /\s*(a\.?m\.?|p\.?m\.?|ص|م)\s*$/i.exec(s);
  if (amPmMatch) {
    const m = amPmMatch[1].toLowerCase().replace(/\./g, "");
    if (m === "am" || m === "ص") isAm = true;
    else if (m === "pm" || m === "م") isPm = true;
    s = s.slice(0, amPmMatch.index).trim();
  }

  if (!s) return null;

  let h: number;
  let mm: number;

  // Compact 3-4 digit form with no separator: "930" -> 9:30, "0930" ->
  // 09:30, "1230" -> 12:30. We branch on length so "930" doesn't get
  // read as 93:0.
  if (/^\d{3,4}$/.test(s)) {
    if (s.length === 3) {
      h = Number(s[0]);
      mm = Number(s.slice(1));
    } else {
      h = Number(s.slice(0, 2));
      mm = Number(s.slice(2));
    }
  } else {
    // Accept ":", ".", "-", or whitespace as the H/M separator. Collapse
    // runs so "9 . 30" still parses cleanly.
    const normalized = s.replace(/[.\-\s]+/g, ":").replace(/:+/g, ":");
    const parts = normalized.split(":");
    if (parts.length !== 2) return null;
    if (!/^\d{1,2}$/.test(parts[0])) return null;
    if (!/^\d{1,2}$/.test(parts[1])) return null;
    h = Number(parts[0]);
    mm = Number(parts[1]);
  }

  if (!Number.isFinite(h) || !Number.isFinite(mm)) return null;
  if (mm < 0 || mm > 59) return null;

  if (isAm || isPm) {
    // 12h shapes only allow 1..12 for the hour. "13:00 PM" is rejected.
    if (h < 1 || h > 12) return null;
    if (isAm) {
      // 12 AM = midnight = 00:xx.
      if (h === 12) h = 0;
    } else {
      // 12 PM = noon = 12:xx; everything else gets +12.
      if (h !== 12) h += 12;
    }
  } else {
    if (h < 0 || h > 23) return null;
  }

  const hStr = String(h).padStart(2, "0");
  const mStr = String(mm).padStart(2, "0");
  return `${hStr}:${mStr}`;
}
