/* CalendarKit — the date-grid engine shared by every Ledger calendar.
   The Trial Calendar and the Asset Requests calendar are two configs of it; a third (crew,
   equipment) should be a config too, not a copy. What lives here is what every
   calendar needs the same way:
     - date helpers, ISO-keyed in local time (no UTC drift)
     - useCalendarNav: Day / Week / Month, an anchor-day cursor, the visible cells,
       prev / next / today, view switching that keeps your place, and a per-calendar
       weekend choice remembered in this browser
     - CalendarHeader: the nav bar (arrows, Today, heading, view toggle) with a slot
       for each calendar's own filters
   What stays in each calendar: what a card looks like, what it loads, its filters.
   Assigns window.LedgerCalendarKit. Load before TrialCalendar.jsx / RequestCalendar.jsx. */
(function () {
  const pad = (n) => String(n).padStart(2, "0");
  const iso = (d) => `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
  const parseISO = (s) => { const [y, m, d] = String(s).split("-").map(Number); return new Date(y, m - 1, d); };
  const addDays = (d, n) => { const x = new Date(d); x.setDate(x.getDate() + n); return x; };
  // Calendar months, clamped: Jan 31 + 1 month is Feb 28, not Mar 3.
  const addMonths = (d, n) => {
    const x = new Date(d.getFullYear(), d.getMonth() + n, 1);
    const last = new Date(x.getFullYear(), x.getMonth() + 1, 0).getDate();
    x.setDate(Math.min(d.getDate(), last));
    return x;
  };
  const startOfMonth = (d) => new Date(d.getFullYear(), d.getMonth(), 1);
  // weekStart: 0 = Sunday (weekends shown), 1 = Monday (weekends hidden, so the
  // hidden Sat/Sun fall AFTER Friday and can roll up into a Friday marker).
  const startOfWeek = (d, weekStart = 0) =>
    addDays(new Date(d.getFullYear(), d.getMonth(), d.getDate()), -((d.getDay() - weekStart + 7) % 7));
  const MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
  const SMON = (i) => MONTHS[i].slice(0, 3);
  const DOW = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
  const shortDate = (s) => parseISO(s).toLocaleDateString(undefined, { month: "short", day: "numeric" });
  // Week header, e.g. "Jun 22 – 28, 2026" / "Jun 29 – Jul 5, 2026".
  const weekRangeLabel = (start, days = 7) => {
    const end = addDays(start, days - 1);
    if (start.getMonth() === end.getMonth()) return `${SMON(start.getMonth())} ${start.getDate()} – ${end.getDate()}, ${end.getFullYear()}`;
    if (start.getFullYear() === end.getFullYear()) return `${SMON(start.getMonth())} ${start.getDate()} – ${SMON(end.getMonth())} ${end.getDate()}, ${end.getFullYear()}`;
    return `${SMON(start.getMonth())} ${start.getDate()}, ${start.getFullYear()} – ${SMON(end.getMonth())} ${end.getDate()}, ${end.getFullYear()}`;
  };
  // "Today" is today in Eastern, L&C's zone, on every calendar.
  const todayISO = () => (window.LedgerTime ? window.LedgerTime.todayEastern() : iso(new Date()));

  const readBool = (key, dflt) => {
    try { const v = localStorage.getItem(key); return v === null ? dflt : v === "1"; } catch (_) { return dflt; }
  };
  const saveBool = (key, v) => { try { localStorage.setItem(key, v ? "1" : "0"); } catch (_) { /* private window */ } };

  // The navigation state of one calendar. Config:
  //   defaultView     "day" | "week" | "month"
  //   weekendKey      localStorage key for this calendar's weekend choice
  //   weekendDefault  shown by default? (Trial: no, Lacey 2026-10-01. FOIA: yes,
  //                   the team parks problem cases on weekends.)
  //   viewKey         optional localStorage key: remember the last view chosen
  function useCalendarNav({ defaultView = "week", weekendKey, weekendDefault = true, viewKey } = {}) {
    const [view, setViewRaw] = React.useState(() => {
      if (!viewKey) return defaultView;
      try { const v = localStorage.getItem(viewKey); return ["day", "week", "month"].includes(v) ? v : defaultView; } catch (_) { return defaultView; }
    });
    const setViewState = (v) => { setViewRaw(v); if (viewKey) { try { localStorage.setItem(viewKey, v); } catch (_) { /* private window */ } } };
    // The cursor is an ANCHOR DAY in every view; each view derives its range from
    // it (the day, its week, its month), so switching views keeps your place.
    const [cursor, setCursor] = React.useState(() => parseISO(todayISO()));
    const [showWeekends, setShowWeekendsState] = React.useState(() => weekendKey ? readBool(weekendKey, weekendDefault) : weekendDefault);
    const setShowWeekends = (v) => { setShowWeekendsState(v); if (weekendKey) saveBool(weekendKey, v); };

    const weekStart = showWeekends ? 0 : 1;
    const nCols = showWeekends ? 7 : 5;
    const isDay = view === "day", isWeek = view === "week";
    const gridStart = isDay ? cursor : isWeek ? startOfWeek(cursor, weekStart) : startOfWeek(startOfMonth(cursor), weekStart);
    const cellCount = isDay ? 1 : isWeek ? 7 : 42;
    const cells = React.useMemo(() => Array.from({ length: cellCount }, (_, i) => addDays(gridStart, i)), [iso(gridStart), cellCount]);
    const weeks = React.useMemo(() => isDay ? [] : Array.from({ length: cellCount / 7 }, (_, w) => cells.slice(w * 7, w * 7 + 7)), [cells, isDay]);
    const dowRow = Array.from({ length: nCols }, (_, i) => DOW[(weekStart + i) % 7]);
    const today = todayISO();
    const from = iso(cells[0]), to = iso(cells[cells.length - 1]);

    const go = (n) => setCursor((c) => isDay ? addDays(c, n) : isWeek ? addDays(c, 7 * n) : new Date(c.getFullYear(), c.getMonth() + n, 1));
    const goToday = () => setCursor(parseISO(today));
    const goTo = (dayISO) => setCursor(parseISO(dayISO));
    // Leaving a wider view for a narrower one lands on TODAY if today is inside
    // what you were looking at, otherwise on the first day of it.
    const setView = (v) => {
      if (v === view) return;
      const t = parseISO(today);
      if (view === "month" && v !== "month") {
        const inMonth = t.getMonth() === cursor.getMonth() && t.getFullYear() === cursor.getFullYear();
        setCursor(inMonth ? t : startOfMonth(cursor));
      } else if (view === "week" && v === "day") {
        setCursor(today >= from && today <= to ? t : startOfWeek(cursor, weekStart));
      }
      setViewState(v);
    };
    const heading = isDay
      ? cursor.toLocaleDateString(undefined, { weekday: "long", month: "long", day: "numeric", year: "numeric" })
      : isWeek ? weekRangeLabel(gridStart, nCols) : `${MONTHS[cursor.getMonth()]} ${cursor.getFullYear()}`;
    const unit = isDay ? "day" : isWeek ? "week" : "month";
    const colStyle = { gridTemplateColumns: `repeat(${nCols}, 1fr)` };

    return { view, setView, isDay, isWeek, cursor, go, goToday, goTo, gridStart, cells, weeks, dowRow,
             from, to, today, heading, unit, showWeekends, setShowWeekends, weekStart, nCols, colStyle };
  }

  // The nav bar. `children` goes on the right, after the view toggle: each
  // calendar's own filters.
  function CalendarHeader({ nav, onNav, children }) {
    const wrap = (fn) => (...a) => { onNav && onNav(); fn(...a); };
    return (
      <div className="cal-bar">
        <div className="cal-nav">
          <button className="cal-navbtn" onClick={wrap(() => nav.go(-1))} title={"Previous " + nav.unit}><i data-lucide="chevron-left" /></button>
          <button className="cal-navbtn" onClick={wrap(() => nav.go(1))} title={"Next " + nav.unit}><i data-lucide="chevron-right" /></button>
          <button className="cal-today" onClick={wrap(nav.goToday)}>Today</button>
          <h2 className="cal-month">{nav.heading}</h2>
        </div>
        <div className="cal-bar-right">
          <div className="cal-viewtoggle" role="tablist">
            {["day", "week", "month"].map((v) => (
              <button key={v} type="button" className={"cal-viewbtn" + (nav.view === v ? " is-on" : "")}
                onClick={wrap(() => nav.setView(v))}>{v[0].toUpperCase() + v.slice(1)}</button>
            ))}
          </div>
          {children}
        </div>
      </div>
    );
  }

  window.LedgerCalendarKit = {
    pad, iso, parseISO, addDays, addMonths, startOfMonth, startOfWeek, MONTHS, SMON, DOW,
    shortDate, weekRangeLabel, todayISO, useCalendarNav, CalendarHeader,
  };
})();
