// Dashboard + the doodle background system. Extracted from app.jsx in Wave 7b.
//
// Exports (via Object.assign(window, { ... }) at bottom):
//   Dashboard, DashboardDoodles, doodleGroupForPath
//
// DashboardDoodles + doodleGroupForPath are exported because Page (in
// helpers.jsx) references them by bare identifier — bare lookup falls back
// through window when there's no lexical match in helpers.jsx's IIFE scope.
//
// File-private (not exported, only used inside this file):
//   prettyDateLong, BG_DIR, BG_FILES, MAX_PER_FILE, _generateDoodleItems,
//   DOODLE_GROUPS, DashCalendarCard, DashUpcomingCard, DashUpNextCard
//
// External deps consumed:
//   from data modules: useAuth, useSubjects, useGuides, useEvents,
//     subjectsLoaded, guidesLoaded, eventsLoaded, todayISO,
//     upcomingEvents, daysBetween, getProfile, pickGreeting
//   from helpers.jsx: Page, navigate, useSkeletonGate, monthCells,
//     monthOffset, NAV_ICONS
//   from Wave 3 promotion: ThemeButton

(function () {

// Dashboard-only formatter. Was at the top of app.jsx; only Dashboard uses it.
function prettyDateLong(iso) {
  return new Date(iso + 'T00:00:00').toLocaleDateString(undefined, { weekday: 'long', month: 'long', day: 'numeric' });
}

// ─── Dashboard ─────────────────────────────────────────────────────────────

// Random scatter of user-supplied background images sitting behind the
// dashboard. Files live in `icons/Dashboard Backgroudn icons/` (folder name
// preserved as-is, including the typo, so URLs match the file system). To
// add or remove images: drop new files into that folder and update the
// BG_FILES list below. Positions/rotations seed once per mount via useMemo.
const BG_DIR = 'icons/Dashboard%20Backgroudn%20icons/';
const BG_FILES = [
  '02d18f077302266605aad3a4c4eef175.png',
  '056f4bce9fe1d4c33b24b34f3360e13d.png',
  '0a61d69ff71d59375e13e252f77d32a3.png',
  '14164b1d77c19c8d937c89c00d71f533.png',
  '1c0624b684dab4cfb0c09afbd34d46ab.png',
  '1f9656d31bf4dcd541aa29e8f1b0117d.png',
  '213e37bc4ead4f85b9c57ee59ae387fe.png',
  '26d5a76ddb43d17f94f7c029753ef68c.png',
  '2a36ce1b433302b3841ef8aa4b9eb727.png',
  '32e982f1c9a3df365d565e7a3ea2cffa.png',
  '41f2818eb02b7cf8c2b102e7627488fc.png',
  '4712fda6157337d2924c598588f936f6.png',
  '500e624403d4ce176c3304f49490811b.png',
  '589348612b05ea834f55ceb3cefcfe36.png',
  '72e60c931de735f4e7398da9267199db.png',
  '7ec173b6287fb176788e836fe1f8bad8.png',
  '8a162d1962c832aa1ef3d6a4364bbcb6.png',
  '8f24eee2f5448759c4842b916c2a40ba.png',
  '8f96cc78123c5f2c25498fea7c08d87a.png',
  '9b3a96d8edcef9edf3fec9d30b727408.png',
  '9d6701b12f1b09ec526746334919e640.png',
  'a567cd5d61824d1245d95f1a554328b4.png',
  'c1590edb5b97b59b824a3648ddac3d30.png',
  'd64ae80f651e42b002cd092cd9da7279.png',
  'd763b75c37fc9cfac8919bb94a9ba613.png',
  'fe5e99cbaa02ce89b737adfc6f283630.png',
  'icon banan.png',
  'icon bear.png',
  'icon.png',
  'no bg cloud 1.png',
  'no bg cloud.png',
  'no bg icon 1.png',
  'no bg icon 2.png',
  'no bg icon 5.png',
  'no bg icon 6.png',
  'no bg icon 9.png',
  'no bg icon.png',
  'spinbob.png',
];
const MAX_PER_FILE = 2; // each icon can appear at most this many times on the dashboard

// Build the doodle layout ONCE per session (module load) and freeze it. Every
// `<DashboardDoodles>` mount across every route reads from the same cache, so
// the icons stay in identical positions as the user navigates between pages.
// A page refresh re-runs the module and generates a fresh layout.
function _generateDoodleItems() {
    // Mitchell's best-candidate sampling: for each new icon, generate K random
    // candidate positions and keep the one whose nearest neighbour is the
    // FURTHEST. This produces uniform-density, blue-noise-style spread (no
    // clusters, no big gaps). Two distance budgets — same-file pairs need a
    // larger separation than different-file pairs.
    const COUNT          = 11; // ~75% of the original 15
    const MIN_DIST_ANY   = 25;  // %-points; floor for any two icons
    const MIN_DIST_SAME  = 50;  // %-points; same file must be far apart
    const K_CANDIDATES   = 30;  // candidates per slot — higher = more even

    // Build the file sequence so any single icon appears at most MAX_PER_FILE
    // times. Shuffled passes of BG_FILES, trimmed.
    const cycle = [];
    for (let pass = 0; pass < MAX_PER_FILE && cycle.length < COUNT; pass++) {
      cycle.push(...BG_FILES.slice().sort(() => Math.random() - 0.5));
    }
    const files = cycle.slice(0, COUNT);

    const out = [];
    for (let i = 0; i < COUNT; i++) {
      const file = files[i];
      let bestTop = 0, bestLeft = 0, bestScore = -Infinity;
      for (let k = 0; k < K_CANDIDATES; k++) {
        // Icons translate by -50%, -50% (anchored at center) and reach ~75px
        // past their position, so a generous % inset is needed to keep them
        // fully on-screen at narrower viewport widths.
        const top  = Math.random() * 70 + 15; // 15..85% — ~15% margin per edge
        const left = Math.random() * 70 + 15;
        // Score this candidate by the smallest distance-to-required ratio
        // across every existing icon. ratio < 1 means "too close per the
        // budget for this pair" — minimised across all pairs is what we want
        // to maximise. First icon scores Infinity.
        let worst = Infinity;
        for (const o of out) {
          const d = Math.hypot(o.leftN - left, o.topN - top);
          const required = (o.file === file) ? MIN_DIST_SAME : MIN_DIST_ANY;
          const ratio = d / required;
          if (ratio < worst) worst = ratio;
        }
        if (worst > bestScore) {
          bestScore = worst;
          bestTop = top;
          bestLeft = left;
        }
      }
      // Rotation: same-file duplicates must differ by at least MIN_ROT_DIFF°
      // so two instances never look like the same sticker. Range widened to
      // -40..40° to give 2+ duplicates room to spread apart in angle.
      const ROT_RANGE    = 80; // -40..40°
      const MIN_ROT_DIFF = 30;
      const sameFileRots = out.filter((o) => o.file === file).map((o) => o.rotate);
      let rotate = 0, bestRotScore = -Infinity;
      for (let r = 0; r < 30; r++) {
        const candidate = Math.floor(Math.random() * ROT_RANGE) - ROT_RANGE / 2;
        const closest = sameFileRots.length
          ? Math.min(...sameFileRots.map((e) => Math.abs(e - candidate)))
          : Infinity;
        if (closest > bestRotScore) {
          bestRotScore = closest;
          rotate = candidate;
        }
        if (closest >= MIN_ROT_DIFF) break;
      }
      out.push({
        file,
        leftN:   bestLeft,
        topN:    bestTop,
        src:     BG_DIR + encodeURIComponent(file),
        top:     bestTop + '%',
        left:    bestLeft + '%',
        rotate,
        size:    130 + Math.floor(Math.random() * 21), // 130–150px
        opacity: 0.32 + Math.random() * 0.20,         // 0.32–0.52
      });
    }
    return out;
}
// Three frozen layouts so the icon background stays consistent within each
// section but differs between sections. Generated once per module load (a page
// refresh produces a new set). Routing in <Page> picks the right group:
//   main:     Dashboard / Calendar / Library / individual guide views
//   subjects: Subject detail + per-subject upload screens
//   notes:    Notes index + per-note editor + note folders
const DOODLE_GROUPS = {
  main:     _generateDoodleItems(),
  subjects: _generateDoodleItems(),
  notes:    _generateDoodleItems(),
};

function doodleGroupForPath(path) {
  if (!path) return 'main';
  // Guide-viewing pages live under /subject/:id/guide/:gid — group with main.
  if (/^\/subject\/[^\/]+\/guide\//.test(path)) return 'main';
  if (path.startsWith('/subject')) return 'subjects';
  if (path.startsWith('/note') || path.startsWith('/folder')) return 'notes';
  return 'main';
}

function DashboardDoodles({ group }) {
  const items = DOODLE_GROUPS[group] || DOODLE_GROUPS.main;
  const groupClass = group ? ` dashboard-doodles--${group}` : '';
  return (
    <div className={"dashboard-doodles" + groupClass} style={{
      position: 'absolute', inset: 0, overflow: 'hidden',
      pointerEvents: 'none', zIndex: -1,
    }}>
      {items.map((d, i) => (
        <img
          key={i}
          src={d.src}
          alt=""
          loading="eager"
          draggable="false"
          style={{
            position: 'absolute', top: d.top, left: d.left,
            width: d.size, height: 'auto',
            transform: `translate(-50%, -50%) rotate(${d.rotate}deg)`,
            opacity: d.opacity,
            userSelect: 'none',
          }}
        />
      ))}
    </div>
  );
}

// The Dashboard layout used to be a draggable react-grid-layout grid with a
// per-user-saved arrangement. It was replaced by the static layout below
// (mirrors the Dashboard.html mock). The old localStorage keys
// (studybuddy:dashboard:layout:* and studybuddy.dashboard.blockOpacity.v2) are
// left in place — they're harmless and clean themselves up when the user's
// browser eventually evicts them.

// ─── Dashboard (static, cream-warm — see Dashboard.html mock) ──────────────
//
// Layout: greeting → subjects strip → 3-stat row → main column (calendar +
// upcoming) + right rail (up-next gradient card + 2×2 quick tiles). Replaces
// the older draggable react-grid-layout variant. All data is live: subjects,
// guides, events, and time-of-day greeting come from the existing modules.

function Dashboard() {
  const auth       = useAuth();
  const subjects   = useSubjects();
  const userGuides = useGuides();
  const events     = useEvents();
  const skStats    = useSkeletonGate(subjectsLoaded() && guidesLoaded());
  const skEvents   = useSkeletonGate(eventsLoaded());

  const today      = todayISO();
  const upcoming   = upcomingEvents(events, 60);
  const next       = upcoming[0];
  const inSeven    = upcoming.filter((e) => daysBetween(today, e.date) <= 7);
  const totalCards = userGuides.reduce(
    (n, g) => n + ((g.content?.flashcards || []).length),
    0,
  );

  // Mini-calendar cursor — initialised to current month/year on mount.
  const [cursor, setCursor] = React.useState(() => {
    const d = new Date();
    return { y: d.getFullYear(), m: d.getMonth() };
  });

  // Name: prefer display_name (first token) → email local-part (capitalised) → 'there'.
  const profile  = getProfile(auth.user);
  const userName = profile.displayName
    ? profile.displayName.split(' ')[0]
    : (profile.email
        ? profile.email.split('@')[0].charAt(0).toUpperCase() + profile.email.split('@')[0].slice(1)
        : 'there');
  // Frozen at mount so the greeting doesn't flip while the dashboard is open.
  const [greeting] = React.useState(() => pickGreeting());

  const handleNewGuide = () => window.openNewGuide?.();
  const handleUpload   = () => window.openUploadFiles?.();

  return (
    <Page
      crumbs={['Dashboard']}
      actions={
        <>
          <ThemeButton />
          <button className="btn" onClick={() => navigate('/calendar')}>Open calendar</button>
          <button className="btn dark" onClick={handleNewGuide}>＋ New guide</button>
        </>
      }
    >
      <div className="cd-dash">
        <div className="cd-hello-wrap">
          <div className="cd-collage" aria-hidden="true">
            <span className="c1"></span><span className="c2"></span><span className="c3"></span><span className="c4"></span>
          </div>
          <div className="cd-eyebrow">{prettyDateLong(today)}</div>
          <h1 className="cd-hello">{greeting}, <em>{userName}.</em></h1>
          <p className="cd-hello-sub">
            You have <b>{inSeven.length}</b> {inSeven.length === 1 ? 'thing' : 'things'} on the calendar this week, and <b>{userGuides.length}</b> guide{userGuides.length === 1 ? '' : 's'} in your library.
          </p>
        </div>

        <div className="cd-stats">
          <div className="cd-stat">
            <div className="lbl">Subjects</div>
            <div className="row">{skStats ? <span className="sk sk-line lg" style={{ width: 40 }} /> : <div className="big">{subjects.length}</div>}<div className="ctx">tracked</div></div>
          </div>
          <div className="cd-stat">
            <div className="lbl">Guides</div>
            <div className="row">{skStats ? <span className="sk sk-line lg" style={{ width: 40 }} /> : <div className="big">{userGuides.length}</div>}<div className="ctx">generated</div></div>
          </div>
          <div className="cd-stat">
            <div className="lbl">Cards</div>
            <div className="row">{skStats ? <span className="sk sk-line lg" style={{ width: 40 }} /> : <div className="big">{totalCards}</div>}<div className="ctx">across decks</div></div>
          </div>
        </div>

        <DashCalendarCard cursor={cursor} setCursor={setCursor} events={events} skLoading={skEvents} />
        <DashUpcomingCard events={upcoming} subjects={subjects} skLoading={skEvents} />

        {/* RIGHT RAIL */}
        <aside className="cd-rail">
          <DashUpNextCard next={next} today={today} subjects={subjects} skLoading={skEvents} />
          <div className="cd-tiles">
            <button className="cd-tile dark" onClick={handleNewGuide}>
              <div className="ic">＋</div>
              <div>
                <div className="h">New guide</div>
                <div className="sub">Upload + generate</div>
              </div>
            </button>
            <button className="cd-tile" onClick={handleUpload}>
              <div className="ic">↑</div>
              <div>
                <div className="h">Upload files</div>
                <div className="sub">Add to a subject</div>
              </div>
            </button>
            <button className="cd-tile" onClick={() => navigate('/library')}>
              <div className="ic">◆</div>
              <div>
                <div className="h">Library</div>
                <div className="sub">{userGuides.length} guide{userGuides.length === 1 ? '' : 's'}</div>
              </div>
            </button>
            <button className="cd-tile" onClick={() => navigate('/calendar')}>
              <div className="ic">{NAV_ICONS.calendar}</div>
              <div>
                <div className="h">Calendar</div>
                <div className="sub">{events.length} event{events.length === 1 ? '' : 's'}</div>
              </div>
            </button>
          </div>
          <div className="cd-rail-decor" aria-hidden="true"></div>
        </aside>
      </div>
    </Page>
  );
}

function DashCalendarCard({ cursor, setCursor, events, skLoading }) {
  const today    = todayISO();
  const monthName = new Date(cursor.y, cursor.m, 1).toLocaleString(undefined, { month: 'long', year: 'numeric' });
  const cells    = React.useMemo(() => monthCells(cursor.y, cursor.m), [cursor.y, cursor.m]);
  const byDate   = React.useMemo(() => {
    const out = {};
    for (const e of events) (out[e.date] = out[e.date] || []).push(e);
    return out;
  }, [events]);

  return (
    <div className="cd-cal">
      <div className="cd-cal-head">
        <div className="cd-cal-title">{monthName}</div>
        <div className="cd-cal-ctrls">
          <button className="cd-icon-btn" onClick={() => setCursor(monthOffset(cursor, -1))} aria-label="Previous month" title="Previous month">{NAV_ICONS.chevLeft}</button>
          <button className="btn" onClick={() => { const d = new Date(); setCursor({ y: d.getFullYear(), m: d.getMonth() }); }} title="Jump to current month">Today</button>
          <button className="cd-icon-btn" onClick={() => setCursor(monthOffset(cursor, 1))} aria-label="Next month" title="Next month">
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M9 18l6-6-6-6"/></svg>
          </button>
          <button className="btn" onClick={() => navigate('/calendar')}>Open →</button>
        </div>
      </div>
      <div className="cd-cal-grid">
        {['Sun','Mon','Tue','Wed','Thu','Fri','Sat'].map((d) => (
          <div key={d} className="cd-dow">{d}</div>
        ))}
        {cells.map((cell, i) => {
          const dayEvents = byDate[cell.date] || [];
          const isToday   = cell.date === today;
          const inMonth   = cell.month === cursor.m;
          return (
            <div
              key={cell.date}
              className={"cd-cell" + (inMonth ? '' : ' dim') + (isToday ? ' today' : '')}
              onClick={() => navigate('/calendar')}
            >
              <div className="num">{cell.day}</div>
              {skLoading && inMonth && dayEvents.length === 0 && (i % 4 === 0) && (
                <span className="sk sk-line sm" style={{ marginTop: 3, width: '75%' }} />
              )}
              {dayEvents.slice(0, 2).map((e) => (
                <div
                  key={e.id}
                  className={"chip" + (e.kind === 'study' || e.kind === 'assignment' ? ' blue' : '')}
                  title={e.title}
                >
                  {e.title}
                </div>
              ))}
            </div>
          );
        })}
      </div>
    </div>
  );
}

function DashUpcomingCard({ events, subjects, skLoading }) {
  const tagClass = (k) =>
    k === 'exam'       ? 'exam'
    : k === 'assignment' ? 'assign'
    : k === 'study'      ? 'study'
    : 'event';
  const tagLabel = (k) =>
    k === 'exam'       ? 'Exam'
    : k === 'assignment' ? 'Assignment'
    : k === 'study'      ? 'Study'
    : 'Event';

  return (
    <div className="cd-upcoming">
      <div className="cd-up-head">
        <div className="cd-up-title">Upcoming</div>
        <button className="cd-up-open" onClick={() => navigate('/calendar')}>Open calendar →</button>
      </div>
      <div className="cd-up-list">
        {skLoading ? (
          Array.from({ length: 4 }).map((_, i) => (
            <div key={i} className="cd-up-row" style={{ pointerEvents: 'none' }}>
              <div className="cd-up-when"><span className="sk sk-line w70" /></div>
              <div className="what"><span className="sk sk-line" /></div>
              <span className="sk sk-line" style={{ width: 60, height: 12, flex: '0 0 auto' }} />
            </div>
          ))
        ) : events.length === 0 ? (
          <div className="cd-up-row" style={{ color: 'var(--cd-muted)', justifyContent: 'center' }}>
            Nothing scheduled.
          </div>
        ) : (
          events.slice(0, 5).map((ev) => {
            const subj = subjects.find((s) => s.id === ev.subject_id);
            const dt   = new Date(ev.date + 'T00:00:00');
            const dateLabel =
              dt.toLocaleDateString(undefined, { month: 'short', day: 'numeric' }) +
              ' · ' +
              dt.toLocaleDateString(undefined, { weekday: 'short' });
            return (
              <div key={ev.id} className="cd-up-row" onClick={() => navigate('/calendar')}>
                <div className="cd-up-when">{dateLabel}</div>
                <div className="what">
                  {subj ? <span style={{ color: 'var(--cd-muted)' }}>{subj.name} — </span> : null}
                  {ev.title}
                </div>
                <span className={"cd-tag " + tagClass(ev.kind)}>{tagLabel(ev.kind)}</span>
              </div>
            );
          })
        )}
      </div>
    </div>
  );
}

function DashUpNextCard({ next, today, subjects, skLoading }) {
  if (skLoading) {
    return (
      <div className="cd-upnext">
        <div className="lbl">Up next</div>
        <div>
          <span className="sk sk-line lg" style={{ width: '70%', marginBottom: 8, background: 'linear-gradient(90deg, rgba(255,255,255,0.10) 0%, rgba(255,255,255,0.20) 50%, rgba(255,255,255,0.10) 100%)' }} />
          <span className="sk sk-line" style={{ width: '45%', background: 'linear-gradient(90deg, rgba(255,255,255,0.08) 0%, rgba(255,255,255,0.16) 50%, rgba(255,255,255,0.08) 100%)' }} />
        </div>
        <div className="when">
          <span className="sk sk-line lg" style={{ width: 60, height: 28, background: 'linear-gradient(90deg, rgba(255,255,255,0.10) 0%, rgba(255,255,255,0.20) 50%, rgba(255,255,255,0.10) 100%)' }} />
        </div>
      </div>
    );
  }
  if (!next) {
    return (
      <div className="cd-upnext">
        <div className="lbl">Up next</div>
        <div><div className="ttl" style={{ opacity: 0.75 }}>Nothing scheduled</div></div>
        <div className="kind">Add an event from the calendar.</div>
      </div>
    );
  }
  const subj = subjects.find((s) => s.id === next.subject_id);
  const days = daysBetween(today, next.date);
  return (
    <div className="cd-upnext">
      <div className="lbl">Up next</div>
      <div>
        <div className="ttl">{next.title}</div>
        <div className="kind">{subj ? `${subj.name} · ${next.kind}` : next.kind}</div>
      </div>
      <div className="when">
        <span className={"num" + (days === 0 ? ' today-word' : '')}>{days === 0 ? 'today' : days + 'd'}</span>
        {days > 0 && <span className="lab">away</span>}
      </div>
    </div>
  );
}

// ─── Exports ───────────────────────────────────────────────────────────────

Object.assign(window, {
  Dashboard,
  DashboardDoodles,
  doodleGroupForPath,
});

})();
