/* ============================================================
   Shared helpers + components
   ============================================================ */
const { useState, useEffect, useRef, useMemo } = React;

/* ---------- Date helpers ---------- */
const MONTHS = ["January","February","March","April","May","June","July","August","September","October","November","December"];
const MONTHS_SHORT = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
const DOW = ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"];

function parseISO(s) { const [y,m,d] = s.split("-").map(Number); return new Date(y, m-1, d); }
function toISO(dt) {
  const y = dt.getFullYear(), m = String(dt.getMonth()+1).padStart(2,"0"), d = String(dt.getDate()).padStart(2,"0");
  return `${y}-${m}-${d}`;
}
function addDays(dt, n) { const c = new Date(dt); c.setDate(c.getDate()+n); return c; }
function isWeekend(dt) { const g = dt.getDay(); return g === 0 || g === 6; }
function eachDay(fromISO_, toISO_) {
  const out = []; let cur = parseISO(fromISO_); const end = parseISO(toISO_);
  while (cur <= end) { out.push(new Date(cur)); cur = addDays(cur, 1); }
  return out;
}
// working days — counts only the requester's contracted workdays; half day = 0.5
function workingDays(req) {
  if (req.type === "half") return 0.5;
  const person = req.person ? VP_DATA.byId[req.person] : null;
  const wd = (person && person.workdays) || [1, 2, 3, 4, 5];
  return eachDay(req.from, req.to).filter((d) => wd.includes(d.getDay())).length;
}
function fmtRange(fromISO_, toISO_) {
  const a = parseISO(fromISO_), b = parseISO(toISO_);
  if (fromISO_ === toISO_) return `${MONTHS_SHORT[a.getMonth()]} ${a.getDate()}`;
  if (a.getMonth() === b.getMonth()) return `${MONTHS_SHORT[a.getMonth()]} ${a.getDate()}–${b.getDate()}`;
  return `${MONTHS_SHORT[a.getMonth()]} ${a.getDate()} – ${MONTHS_SHORT[b.getMonth()]} ${b.getDate()}`;
}
function fmtDateLong(iso) { const d = parseISO(iso); return `${DOW[(d.getDay()+6)%7]}, ${MONTHS_SHORT[d.getMonth()]} ${d.getDate()}`; }
function daysAgo(iso) {
  const now = new Date(2026, 4, 29); // "today" in demo
  const diff = Math.round((now - parseISO(iso)) / 86400000);
  if (diff <= 0) return "today";
  if (diff === 1) return "yesterday";
  if (diff < 7) return `${diff}d ago`;
  return `${Math.floor(diff/7)}w ago`;
}
const TODAY = "2026-05-29";

/* ---------- Icons (stroke line icons) ---------- */
function Icon({ name, size = 18, stroke = 1.6, style }) {
  const common = { width: size, height: size, viewBox: "0 0 24 24", fill: "none",
    stroke: "currentColor", strokeWidth: stroke, strokeLinecap: "round", strokeLinejoin: "round", style };
  const P = {
    sun: <g><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4 12H2M22 12h-2M5 5l1.5 1.5M17.5 17.5L19 19M19 5l-1.5 1.5M6.5 17.5L5 19"/></g>,
    home: <g><path d="M3 10.5 12 3l9 7.5"/><path d="M5 9.5V21h14V9.5"/><path d="M9.5 21v-6h5v6"/></g>,
    thermo: <g><path d="M10 13.5V5a2 2 0 1 1 4 0v8.5a4 4 0 1 1-4 0Z"/><path d="M12 9v5.5"/></g>,
    half: <g><circle cx="12" cy="12" r="9"/><path d="M12 3a9 9 0 0 1 0 18Z" fill="currentColor" stroke="none"/></g>,
    star: <g><path d="M12 3.5l2.4 5 5.6.7-4 3.9 1 5.5L12 16l-5 2.6 1-5.5-4-3.9 5.6-.7Z"/></g>,
    calendar: <g><rect x="3" y="4.5" width="18" height="16" rx="2.5"/><path d="M3 9h18M8 2.5v4M16 2.5v4"/></g>,
    inbox: <g><path d="M3 13h5l1.5 2.5h5L16 13h5"/><path d="M5 5h14l2 8v5a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-5Z"/></g>,
    users: <g><circle cx="9" cy="8" r="3.2"/><path d="M3.5 19c.6-3 3-4.5 5.5-4.5S14 16 14.5 19"/><path d="M16 5.2a3 3 0 0 1 0 5.6M17 14.6c2 .5 3.6 2 4 4.4"/></g>,
    plus: <g><path d="M12 5v14M5 12h14"/></g>,
    check: <g><path d="M5 12.5 10 17 19 7"/></g>,
    x: <g><path d="M6 6l12 12M18 6 6 18"/></g>,
    arrowRight: <g><path d="M5 12h14M13 6l6 6-6 6"/></g>,
    arrowLeft: <g><path d="M19 12H5M11 18l-6-6 6-6"/></g>,
    chevDown: <g><path d="M6 9l6 6 6-6"/></g>,
    chevLeft: <g><path d="M15 6l-6 6 6 6"/></g>,
    chevRight: <g><path d="M9 6l6 6-6 6"/></g>,
    clock: <g><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3.5 2"/></g>,
    swap: <g><path d="M7 4 3 8l4 4M3 8h12M17 20l4-4-4-4M21 16H9"/></g>,
    logout: <g><path d="M14 4h4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-4"/><path d="M9 12h11M16 8l4 4-4 4"/></g>,
    bell: <g><path d="M6 9a6 6 0 0 1 12 0c0 5 1.5 6.5 2 7H4c.5-.5 2-2 2-7Z"/><path d="M10 20a2 2 0 0 0 4 0"/></g>,
    search: <g><circle cx="11" cy="11" r="7"/><path d="m20 20-3.2-3.2"/></g>,
    info: <g><circle cx="12" cy="12" r="9"/><path d="M12 11v5M12 7.5v.5"/></g>,
    dot: <circle cx="12" cy="12" r="3" fill="currentColor" stroke="none"/>,
    leaf: <g><path d="M5 19c0-7 5-13 14-14 0 9-5 14-14 14Z"/><path d="M5 19c2-5 5-8 9-10"/></g>,
  };
  return <svg {...common} aria-hidden="true">{P[name] || P.dot}</svg>;
}

/* ---------- Mesper logo mark (M + upward arrow) ---------- */
function MMark({ size = 30, radius, gradient = true, color }) {
  return (
    <div style={{
      width: size, height: size, borderRadius: radius != null ? radius : Math.round(size * 0.27),
      background: gradient ? "linear-gradient(135deg, #3490EB 0%, #5A58E7 55%, #7D50CA 100%)" : (color || "var(--accent)"),
      display: "grid", placeItems: "center", flex: "0 0 auto", boxShadow: "var(--sh-sm)",
    }}>
      <svg width={size * 0.6} height={size * 0.6} viewBox="0 0 24 24" fill="none"
        stroke="#fff" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
        <polyline points="4.5,19 4.5,8.5 12,4.2 19.5,8.5 19.5,19" />
      </svg>
    </div>
  );
}

/* ---------- Mesper wordmark ---------- */
function Wordmark({ size = 18, light = false, sub = "Leave" }) {
  return (
    <span style={{ fontFamily: "var(--font-display)", fontWeight: 800, fontSize: size,
      letterSpacing: "0.06em", color: light ? "#fff" : "var(--ink)", lineHeight: 1, whiteSpace: "nowrap" }}>
      MESPER{sub && <span style={{ fontWeight: 600, letterSpacing: "0.02em", color: light ? "rgba(255,255,255,0.7)" : "var(--ink-3)" }}>&nbsp;{sub}</span>}
    </span>
  );
}

/* ---------- Avatar ---------- */
function initials(p) { return (p.first[0] + p.last[0]).toUpperCase(); }
function Avatar({ person, size = 36, ring = false }) {
  if (!person) return null;
  return (
    <div style={{
      width: size, height: size, borderRadius: "50%", flex: "0 0 auto",
      display: "grid", placeItems: "center",
      background: person.color, color: "#fff",
      fontFamily: "var(--font-display)", fontWeight: 600,
      fontSize: size * 0.38, letterSpacing: "-0.02em",
      boxShadow: ring ? "0 0 0 3px var(--surface), 0 0 0 4px var(--line-2)" : "none",
      userSelect: "none",
    }}>{initials(person)}</div>
  );
}

/* ---------- Type tag / pill ---------- */
function TypeTag({ type, withLabel = true, size = "md" }) {
  const lt = VP_DATA.leaveTypes[type];
  const pad = size === "sm" ? "3px 9px 3px 7px" : "5px 12px 5px 9px";
  const fs = size === "sm" ? 12 : 13;
  return (
    <span style={{
      display: "inline-flex", alignItems: "center", gap: 6,
      background: lt.soft, color: lt.color, padding: pad,
      borderRadius: "var(--r-pill)", fontWeight: 600, fontSize: fs,
      lineHeight: 1, whiteSpace: "nowrap",
    }}>
      <Icon name={lt.icon} size={size === "sm" ? 13 : 14} stroke={1.8} />
      {withLabel && lt.label}
    </span>
  );
}

/* ---------- Status pill ---------- */
function StatusPill({ status }) {
  const map = {
    pending:  { c: "var(--st-pending)",  b: "var(--st-pending-soft)",  t: "Pending" },
    approved: { c: "var(--st-approved)", b: "var(--st-approved-soft)", t: "Approved" },
    declined: { c: "var(--st-declined)", b: "var(--st-declined-soft)", t: "Declined" },
  };
  const s = map[status];
  return (
    <span style={{
      display: "inline-flex", alignItems: "center", gap: 6,
      background: s.b, color: s.c, padding: "4px 11px 4px 9px",
      borderRadius: "var(--r-pill)", fontWeight: 600, fontSize: 12.5, lineHeight: 1,
    }}>
      <span style={{ width: 6, height: 6, borderRadius: "50%", background: "currentColor" }} />
      {s.t}
    </span>
  );
}

/* ---------- Button ---------- */
function Button({ children, variant = "primary", size = "md", icon, iconRight, onClick, disabled, style, type, full }) {
  const [hover, setHover] = useState(false);
  const sizes = {
    sm: { padding: "7px 13px", fontSize: 13.5, gap: 6 },
    md: { padding: "10px 17px", fontSize: 14.5, gap: 8 },
    lg: { padding: "13px 22px", fontSize: 15.5, gap: 9 },
  };
  const variants = {
    primary: { background: "var(--accent)", color: "var(--accent-contrast)", border: "1px solid transparent",
      boxShadow: hover ? "var(--sh-md)" : "var(--sh-sm)", filter: hover ? "brightness(1.06)" : "none" },
    ghost: { background: hover ? "var(--paper-2)" : "transparent", color: "var(--ink-2)", border: "1px solid transparent" },
    outline: { background: hover ? "var(--surface-2)" : "var(--surface)", color: "var(--ink)",
      border: "1px solid var(--line-2)", boxShadow: hover ? "var(--sh-sm)" : "none" },
    approve: { background: hover ? "var(--st-approved)" : "var(--st-approved-soft)", color: hover ? "#fff" : "var(--st-approved)",
      border: "1px solid transparent" },
    decline: { background: hover ? "var(--st-declined)" : "var(--surface)", color: hover ? "#fff" : "var(--st-declined)",
      border: "1px solid var(--st-declined-soft)" },
  };
  return (
    <button type={type || "button"} onClick={disabled ? undefined : onClick} disabled={disabled}
      onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
      className="vp-focusable"
      style={{
        display: "inline-flex", alignItems: "center", justifyContent: "center",
        gap: sizes[size].gap, ...sizes[size], ...variants[variant],
        width: full ? "100%" : "auto",
        borderRadius: "var(--r-sm)", fontFamily: "var(--font-body)", fontWeight: 600,
        cursor: disabled ? "not-allowed" : "pointer", opacity: disabled ? 0.5 : 1,
        whiteSpace: "nowrap",
        transition: "all 0.16s ease", letterSpacing: "-0.01em", ...style,
      }}>
      {icon && <Icon name={icon} size={sizes[size].fontSize + 2} stroke={1.9} />}
      {children}
      {iconRight && <Icon name={iconRight} size={sizes[size].fontSize + 2} stroke={1.9} />}
    </button>
  );
}

/* ---------- Card ---------- */
function Card({ children, style, pad = "var(--pad-card)", className = "" }) {
  return (
    <div className={className} style={{
      background: "var(--surface)", border: "1px solid var(--line)",
      borderRadius: "var(--r-lg)", padding: pad, boxShadow: "var(--sh-sm)", ...style,
    }}>{children}</div>
  );
}

/* ---------- Section label ---------- */
function Eyebrow({ children, style }) {
  return <div style={{
    fontSize: 11.5, fontWeight: 700, letterSpacing: "0.09em", textTransform: "uppercase",
    color: "var(--ink-4)", ...style,
  }}>{children}</div>;
}

/* ---------- Responsive hook ---------- */
function useIsNarrow(bp = 820) {
  const [narrow, setNarrow] = useState(typeof window !== "undefined" && window.innerWidth < bp);
  useEffect(() => {
    const onResize = () => setNarrow(window.innerWidth < bp);
    onResize();
    window.addEventListener("resize", onResize);
    return () => window.removeEventListener("resize", onResize);
  }, [bp]);
  return narrow;
}

/* export */
Object.assign(window, {
  MONTHS, MONTHS_SHORT, DOW, parseISO, toISO, addDays, isWeekend, eachDay,
  workingDays, fmtRange, fmtDateLong, daysAgo, TODAY,
  Icon, initials, Avatar, TypeTag, StatusPill, Button, Card, Eyebrow, MMark, Wordmark,
  useIsNarrow,
});
