/* global React, Card, Button, Segmented, Pill, Eyebrow, SectionHeader */
// ============================================================================
// life.jsx — Tab 4 · Life (_spec-app-v2.md): what the plan can't know.
//   School      — tests and deadlines, reading, grades, the weekly rhythm
//   Sports      — the teams' weekly practice, games / cancellations / travel
//   Activities  — the activity list a college application will ask for
// All data through window.PlanAPI. Writes only when PlanAPI.canWrite(memberDb);
// calendar events: a boy writes his own games and cancellations (023), parents write all.
// ============================================================================
(() => {
  const { useState, useEffect, useCallback } = React;
  const A = () => window.PlanAPI;

  // ---------------------------------------------------------------- helpers
  const WD_SHORT = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
  const hhmm = (t) => (t ? String(t).slice(0, 5) : '');
  function fmtDate(s) {
    if (!s) return '';
    const d = new Date(s + 'T12:00:00');
    return `${WD_SHORT[(d.getDay() + 6) % 7]} ${d.getDate()} ${d.toLocaleString('en-GB', { month: 'short' })}`;
  }
  function fmtLongDate(s) {
    if (!s) return '';
    const d = new Date(s + 'T12:00:00');
    return `${d.getDate()} ${d.toLocaleString('en-GB', { month: 'short' })} ${d.getFullYear()}`;
  }
  function daysBetween(a, b) {
    return Math.round((new Date(b + 'T12:00:00') - new Date(a + 'T12:00:00')) / 864e5);
  }
  function relDay(s) {
    const n = daysBetween(A().today(), s);
    if (n === 0) return 'today';
    if (n === 1) return 'tomorrow';
    if (n === -1) return 'yesterday';
    return n > 0 ? `in ${n} days` : `${-n} days ago`;
  }
  const errMsg = (e) => (e && e.message) || String(e);

  // Load a list through PlanAPI; reload after every write.
  function useLoad(fn, deps) {
    const [s, setS] = useState({ loading: true, error: null, data: null });
    const load = useCallback(async () => {
      setS(x => ({ ...x, loading: true, error: null }));
      try { setS({ loading: false, error: null, data: await fn() }); }
      catch (e) { setS(x => ({ loading: false, error: errMsg(e), data: x.data })); }
    }, deps); // eslint-disable-line react-hooks/exhaustive-deps
    useEffect(() => { load(); }, [load]);
    return { ...s, reload: load };
  }

  // Run a write, then reload; errors land in a small message.
  function useWrite(reload) {
    const [busy, setBusy] = useState(false);
    const [error, setError] = useState(null);
    const run = async (fn) => {
      setBusy(true); setError(null);
      try { await fn(); await reload(); return true; }
      catch (e) { setError(errMsg(e)); return false; }
      finally { setBusy(false); }
    };
    return { busy, error, run, clear: () => setError(null) };
  }

  // ---------------------------------------------------------------- small UI pieces
  const field = {
    display: 'block', width: '100%', minWidth: 0, boxSizing: 'border-box', padding: '9px 10px',
    border: '1px solid var(--border-strong)', borderRadius: 6, background: 'var(--bg-raised)', color: 'var(--fg)',
    fontFamily: 'var(--font-sans)', fontSize: 16, lineHeight: 1.3,
  };
  const grid2 = { display: 'grid', gridTemplateColumns: 'minmax(0,1fr) minmax(0,1fr)', gap: 10 };
  const linkBtn = { background: 'none', border: 'none', color: 'var(--signal)', cursor: 'pointer', fontSize: 13, padding: 0, fontFamily: 'var(--font-sans)' };

  function Field({ label, children, style }) {
    return (
      <label style={{ display: 'block', minWidth: 0, ...style }}>
        <div style={{ fontSize: 12, color: 'var(--fg-muted)', marginBottom: 4 }}>{label}</div>
        {children}
      </label>
    );
  }
  function TextIn({ value, onChange, placeholder, type = 'text', inputMode }) {
    return <input type={type} inputMode={inputMode} value={value ?? ''} placeholder={placeholder}
                  onChange={e => onChange(e.target.value)} style={field} />;
  }
  function Select({ value, onChange, options }) {
    return (
      <select value={value ?? ''} onChange={e => onChange(e.target.value)} style={{ ...field, appearance: 'auto' }}>
        {options.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
      </select>
    );
  }
  function Check({ checked, onChange, label }) {
    return (
      <label style={{ display: 'inline-flex', alignItems: 'center', gap: 8, fontSize: 14, cursor: 'pointer' }}>
        <input type="checkbox" checked={!!checked} onChange={e => onChange(e.target.checked)} style={{ width: 18, height: 18 }} />
        {label}
      </label>
    );
  }
  function Hint({ children }) {
    return <div style={{ fontSize: 12, color: 'var(--fg-subtle)', margin: '6px 0 12px', lineHeight: 1.45 }}>{children}</div>;
  }
  function ErrorLine({ children }) {
    if (!children) return null;
    return <div style={{ fontSize: 13, color: 'var(--danger)', background: 'var(--danger-tint)', borderRadius: 6, padding: '8px 10px', margin: '8px 0' }}>Could not save or load: {children}</div>;
  }
  function Empty({ children }) {
    return <div style={{ fontSize: 13, color: 'var(--fg-muted)', padding: '10px 0' }}>{children}</div>;
  }
  function Loading() {
    return <div style={{ fontSize: 13, color: 'var(--fg-muted)', padding: '10px 0' }}>Loading…</div>;
  }
  function Section({ eyebrow, title, action, children }) {
    return (
      <section style={{ marginTop: 28 }}>
        <SectionHeader eyebrow={eyebrow} title={title} action={action} style={{ marginBottom: 10 }} />
        {children}
      </section>
    );
  }
  function AddToggle({ open, onClick, label = 'Add' }) {
    return <Button kind={open ? 'ghost' : 'secondary'} size="sm" onClick={onClick}>{open ? 'Close' : label}</Button>;
  }
  function Stat3({ items }) {
    return (
      <div style={{ display: 'grid', gridTemplateColumns: `repeat(${items.length}, minmax(0,1fr))`, gap: 8, marginBottom: 12 }}>
        {items.map(it => (
          <Card key={it.label} pad={12}>
            <Eyebrow>{it.label}</Eyebrow>
            <div style={{ fontSize: it.small ? 14 : 22, fontWeight: 500, marginTop: 6, fontVariantNumeric: 'tabular-nums',
                          overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: it.small ? 'normal' : 'nowrap', wordBreak: 'break-word' }}>
              {it.value}
            </div>
          </Card>
        ))}
      </div>
    );
  }

  // ================================================================= SCHOOL
  const SCHOOL_KINDS = [{ value: 'test', label: 'Test' }, { value: 'deadline', label: 'Deadline' }, { value: 'homework', label: 'Homework' }];
  const SUBJECTS = ['Maths', 'English', 'Swedish', 'Science', 'Social studies', 'Language', 'Other'];
  const KIND_PILL = {
    test: { color: 'var(--signal)', bg: 'var(--signal-tint)' },
    deadline: { color: 'var(--warning)', bg: 'var(--warning-tint)' },
    homework: { color: 'var(--fg-muted)', bg: 'var(--ink-50)' },
  };

  function SchoolTab({ memberDb, write }) {
    return (
      <>
        <TestsAndDeadlines memberDb={memberDb} write={write} />
        <Reading memberDb={memberDb} write={write} />
        <Grades memberDb={memberDb} write={write} />
        <WeeklyRhythm />
      </>
    );
  }

  function TestsAndDeadlines({ memberDb, write }) {
    const L = useLoad(() => A().school.list(memberDb), [memberDb]);
    const W = useWrite(L.reload);
    const [open, setOpen] = useState(false);
    const [showPast, setShowPast] = useState(false);
    const blank = { kind: 'test', subject: '', title: '', due_on: A().today() };
    const [f, setF] = useState(blank);
    const set = (k) => (v) => setF(x => ({ ...x, [k]: v }));

    const today = A().today();
    const items = L.data || [];
    const upcoming = items.filter(i => i.due_on >= today && !i.done).sort((a, b) => a.due_on.localeCompare(b.due_on));
    const past = items.filter(i => !(i.due_on >= today && !i.done)).sort((a, b) => b.due_on.localeCompare(a.due_on));
    const pastShown = showPast ? past : past.slice(0, 5);

    async function save() {
      const ok = await W.run(() => A().school.add(memberDb, {
        kind: f.kind, subject: f.subject.trim(), title: f.title.trim() || null, due_on: f.due_on,
      }));
      if (ok) { setF(blank); setOpen(false); }
    }
    const ready = f.subject.trim() && f.due_on && !W.busy;

    return (
      <Section eyebrow="School" title="Tests and deadlines"
               action={write && <AddToggle open={open} onClick={() => setOpen(!open)} />}>
        <Hint>Review time is placed in the homework blocks the 5 school days before a test.</Hint>
        {open && write && (
          <Card pad={14} style={{ marginBottom: 12 }}>
            <Field label="Kind">
              <Segmented options={SCHOOL_KINDS} value={f.kind} onChange={set('kind')} size="sm" />
            </Field>
            <Field label="Subject" style={{ marginTop: 10 }}>
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginBottom: 6 }}>
                {SUBJECTS.map(s => (
                  <button key={s} onClick={(e) => { e.preventDefault(); set('subject')(s); }} style={{
                    border: `1px solid ${f.subject === s ? 'var(--ink-950)' : 'var(--border)'}`, borderRadius: 999,
                    background: f.subject === s ? 'var(--ink-950)' : 'var(--bg-raised)', color: f.subject === s ? '#fff' : 'var(--fg-muted)',
                    padding: '5px 10px', fontSize: 12, cursor: 'pointer', fontFamily: 'var(--font-sans)',
                  }}>{s}</button>
                ))}
              </div>
              <TextIn value={f.subject} onChange={set('subject')} placeholder="Or type a subject" />
            </Field>
            <Field label="What" style={{ marginTop: 10 }}>
              <TextIn value={f.title} onChange={set('title')} placeholder="e.g. Fractions chapter 3" />
            </Field>
            <Field label="Due" style={{ marginTop: 10 }}>
              <TextIn type="date" value={f.due_on} onChange={set('due_on')} />
            </Field>
            <div style={{ marginTop: 12 }}><Button disabled={!ready} onClick={save}>{W.busy ? 'Saving…' : 'Add'}</Button></div>
          </Card>
        )}
        <ErrorLine>{L.error || W.error}</ErrorLine>
        {L.loading && !L.data ? <Loading /> : (
          <>
            {upcoming.length === 0 && <Empty>Nothing coming up. Add tests and deadlines at Sunday planning.</Empty>}
            {upcoming.map(i => <SchoolRow key={i.id} i={i} write={write} busy={W.busy}
                                          onToggle={() => W.run(() => A().school.toggle(i.id, !i.done))} />)}
            {past.length > 0 && (
              <div style={{ marginTop: 14 }}>
                <Eyebrow style={{ marginBottom: 6 }}>Done and past</Eyebrow>
                {pastShown.map(i => <SchoolRow key={i.id} i={i} dim write={write} busy={W.busy}
                                               onToggle={() => W.run(() => A().school.toggle(i.id, !i.done))} />)}
                {past.length > 5 && (
                  <button style={linkBtn} onClick={() => setShowPast(!showPast)}>
                    {showPast ? 'Show fewer' : `Show all ${past.length}`}
                  </button>
                )}
              </div>
            )}
          </>
        )}
      </Section>
    );
  }

  function SchoolRow({ i, dim, write, busy, onToggle }) {
    const overdue = !i.done && i.due_on < A().today();
    const p = KIND_PILL[i.kind] || KIND_PILL.homework;
    return (
      <div style={{
        display: 'flex', gap: 12, alignItems: 'flex-start', padding: '10px 12px', marginBottom: 6,
        border: '1px solid var(--border)', borderRadius: 8, background: dim ? 'var(--ink-25)' : 'var(--bg-raised)',
        opacity: dim ? 0.7 : 1,
      }}>
        <input type="checkbox" checked={!!i.done} disabled={!write || busy} onChange={onToggle}
               aria-label="Done" style={{ width: 20, height: 20, marginTop: 2, flexShrink: 0 }} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', alignItems: 'center' }}>
            <Pill color={p.color} bg={p.bg}>{i.kind}</Pill>
            <span style={{ fontSize: 13, color: 'var(--fg-muted)' }}>{i.subject}</span>
            {overdue && <Pill color="var(--danger)" bg="var(--danger-tint)">overdue</Pill>}
          </div>
          {i.title && <div style={{ fontSize: 15, marginTop: 4, textDecoration: i.done ? 'line-through' : 'none', wordBreak: 'break-word' }}>{i.title}</div>}
        </div>
        <div style={{ textAlign: 'right', flexShrink: 0 }}>
          <div style={{ fontSize: 13, fontFamily: 'var(--font-mono)' }}>{fmtDate(i.due_on)}</div>
          <div style={{ fontSize: 11, color: 'var(--fg-subtle)', marginTop: 2 }}>{relDay(i.due_on)}</div>
        </div>
      </div>
    );
  }

  function Reading({ memberDb, write }) {
    const L = useLoad(() => A().reading.list(memberDb), [memberDb]);
    const W = useWrite(L.reload);
    const [open, setOpen] = useState(false);
    const logs = L.data || [];
    const latestBook = (logs.find(r => r.book && r.book.trim()) || {}).book || '';
    const blank = () => ({ day: A().today(), book: latestBook, minutes: '', finished: false });
    const [f, setF] = useState(blank);
    const set = (k) => (v) => setF(x => ({ ...x, [k]: v }));

    const monday = A().mondayOf(A().today()), sunday = A().addDays(monday, 6);
    const S = A().SEASON;
    const weekMinutes = logs.filter(r => r.day >= monday && r.day <= sunday).reduce((a, r) => a + (Number(r.minutes) || 0), 0);
    const booksFinished = new Set(logs.filter(r => r.finished && r.day >= S.start && r.day <= S.end)
      .map(r => (r.book || '').trim().toLowerCase() || r.id)).size;

    const mins = f.minutes === '' ? null : Number(String(f.minutes).replace(',', '.'));
    const minsOk = mins === null || (Number.isFinite(mins) && mins >= 0 && mins <= 600);
    const ready = f.day && (f.book.trim() || mins) && minsOk && !W.busy;

    async function save() {
      const ok = await W.run(() => A().reading.add(memberDb, {
        day: f.day, book: f.book.trim() || null, minutes: mins === null ? null : Math.round(mins), finished: !!f.finished,
      }));
      if (ok) { setOpen(false); }
    }

    return (
      <Section eyebrow="School" title="Reading"
               action={write && <AddToggle open={open} onClick={() => { if (!open) setF(blank()); setOpen(!open); }} label="Log reading" />}>
        <Stat3 items={[
          { label: 'This week', value: `${weekMinutes} min` },
          { label: 'Books this season', value: booksFinished },
          { label: 'Reading now', value: latestBook || '–', small: true },
        ]} />
        {open && write && (
          <Card pad={14} style={{ marginBottom: 12 }}>
            <div style={grid2}>
              <Field label="Date"><TextIn type="date" value={f.day} onChange={set('day')} /></Field>
              <Field label="Minutes"><TextIn type="number" inputMode="numeric" value={f.minutes} onChange={set('minutes')} placeholder="20" /></Field>
            </div>
            <Field label="Book" style={{ marginTop: 10 }}><TextIn value={f.book} onChange={set('book')} placeholder="Title" /></Field>
            <div style={{ marginTop: 10 }}><Check checked={f.finished} onChange={set('finished')} label="I finished the book" /></div>
            {!minsOk && <div style={{ fontSize: 12, color: 'var(--danger)', marginTop: 6 }}>Minutes: a number from 0 to 600.</div>}
            <div style={{ marginTop: 12 }}><Button disabled={!ready} onClick={save}>{W.busy ? 'Saving…' : 'Save'}</Button></div>
          </Card>
        )}
        <ErrorLine>{L.error || W.error}</ErrorLine>
        {L.loading && !L.data ? <Loading /> : logs.length === 0 ? <Empty>No reading logged yet.</Empty> : (
          <Card pad={0}>
            {logs.slice(0, 8).map((r, n) => (
              <div key={r.id} style={{ display: 'flex', gap: 10, alignItems: 'baseline', padding: '9px 12px', borderTop: n ? '1px solid var(--border)' : 'none', fontSize: 14 }}>
                <span style={{ width: 84, flexShrink: 0, fontFamily: 'var(--font-mono)', fontSize: 12, color: 'var(--fg-muted)' }}>{fmtDate(r.day)}</span>
                <span style={{ flex: 1, minWidth: 0, wordBreak: 'break-word' }}>{r.book || '–'}{r.finished && <span style={{ color: 'var(--success)' }}> · finished</span>}</span>
                <span style={{ fontFamily: 'var(--font-mono)', fontSize: 12, color: 'var(--fg-muted)', flexShrink: 0 }}>{r.minutes != null ? `${r.minutes} min` : ''}</span>
              </div>
            ))}
          </Card>
        )}
      </Section>
    );
  }

  const GRADE_OPTIONS = ['A', 'B', 'C', 'D', 'E', 'F'];
  function termKey(t) {
    const m = String(t || '').match(/^(HT|VT)\s*(\d{4})/i);
    if (!m) return 0;
    return Number(m[2]) * 10 + (m[1].toUpperCase() === 'HT' ? 5 : 1);
  }
  function currentTerm() {
    const d = new Date();
    return d.getMonth() >= 6 ? `HT ${d.getFullYear()}` : `VT ${d.getFullYear()}`;
  }

  function Grades({ memberDb, write }) {
    const L = useLoad(() => A().grades.list(memberDb), [memberDb]);
    const W = useWrite(L.reload);
    const [open, setOpen] = useState(false);
    const blank = { term: currentTerm(), subject: '', grade: '' };
    const [f, setF] = useState(blank);
    const set = (k) => (v) => setF(x => ({ ...x, [k]: v }));

    const rows = L.data || [];
    const terms = [...new Set(rows.map(r => r.term))].sort((a, b) => termKey(b) - termKey(a) || String(b).localeCompare(String(a)));
    const ready = f.term.trim() && f.subject.trim() && f.grade.trim() && !W.busy;

    async function save() {
      const ok = await W.run(() => A().grades.set(memberDb, f.term.trim(), f.subject.trim(), f.grade.trim()));
      if (ok) setF(x => ({ ...x, subject: '', grade: '' }));
    }
    const edit = (r) => { setF({ term: r.term, subject: r.subject, grade: r.grade }); setOpen(true); };

    return (
      <Section eyebrow="School" title="Grades"
               action={write && <AddToggle open={open} onClick={() => setOpen(!open)} label="Set a grade" />}>
        {open && write && (
          <Card pad={14} style={{ marginBottom: 12 }}>
            <div style={grid2}>
              <Field label="Term"><TextIn value={f.term} onChange={set('term')} placeholder="HT 2026" /></Field>
              <Field label="Subject"><TextIn value={f.subject} onChange={set('subject')} placeholder="Maths" /></Field>
            </div>
            <Field label="Grade" style={{ marginTop: 10 }}>
              <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', marginBottom: 6 }}>
                {GRADE_OPTIONS.map(g => (
                  <button key={g} onClick={(e) => { e.preventDefault(); set('grade')(g); }} style={{
                    width: 38, height: 36, borderRadius: 6, cursor: 'pointer', fontSize: 15, fontFamily: 'var(--font-sans)',
                    border: `1px solid ${f.grade === g ? 'var(--ink-950)' : 'var(--border-strong)'}`,
                    background: f.grade === g ? 'var(--ink-950)' : 'var(--bg-raised)', color: f.grade === g ? '#fff' : 'var(--fg)',
                  }}>{g}</button>
                ))}
              </div>
              <TextIn value={f.grade} onChange={set('grade')} placeholder="Or type, e.g. Pass" />
            </Field>
            <Hint>Setting a grade for the same term and subject replaces the old one.</Hint>
            <Button disabled={!ready} onClick={save}>{W.busy ? 'Saving…' : 'Save grade'}</Button>
          </Card>
        )}
        <ErrorLine>{L.error || W.error}</ErrorLine>
        {L.loading && !L.data ? <Loading /> : terms.length === 0 ? <Empty>No grades yet. They come at the end of each term.</Empty> : (
          terms.map(t => (
            <Card key={t} pad={0} style={{ marginBottom: 10 }}>
              <div style={{ padding: '10px 12px 6px' }}><Eyebrow>{t}</Eyebrow></div>
              {rows.filter(r => r.term === t).sort((a, b) => a.subject.localeCompare(b.subject)).map(r => (
                <div key={r.id} onClick={write ? () => edit(r) : undefined} style={{
                  display: 'flex', justifyContent: 'space-between', gap: 10, padding: '8px 12px', borderTop: '1px solid var(--border)',
                  fontSize: 14, cursor: write ? 'pointer' : 'default',
                }}>
                  <span style={{ minWidth: 0, wordBreak: 'break-word' }}>{r.subject}</span>
                  <span style={{ fontWeight: 600, fontFamily: 'var(--font-mono)' }}>{r.grade}</span>
                </div>
              ))}
            </Card>
          ))
        )}
      </Section>
    );
  }

  const RHYTHM = [
    { when: 'Sunday', what: 'Planning: check Unikum, list this week’s tests and deadlines here.' },
    { when: 'Every day', what: 'Homework before practice, not after.' },
    { when: '5 school days out', what: 'Test prep starts: a little review in each homework block.' },
    { when: 'Friday', what: 'Wrap: tick off what is done, note what moves to next week.' },
  ];
  function WeeklyRhythm() {
    return (
      <Section eyebrow="School" title="The weekly rhythm">
        <Card pad={0}>
          {RHYTHM.map((r, n) => (
            <div key={r.when} style={{ display: 'flex', gap: 12, padding: '11px 12px', borderTop: n ? '1px solid var(--border)' : 'none' }}>
              <span style={{ width: 18, height: 18, borderRadius: 4, border: '1.5px solid var(--border-strong)', flexShrink: 0, marginTop: 1 }} />
              <div style={{ minWidth: 0 }}>
                <div style={{ fontSize: 12, color: 'var(--fg-subtle)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>{r.when}</div>
                <div style={{ fontSize: 14, marginTop: 2 }}>{r.what}</div>
              </div>
            </div>
          ))}
        </Card>
      </Section>
    );
  }

  // ================================================================= SPORTS
  function sportOf(b) {
    const l = String(b.label || '');
    if (/hand/i.test(l)) return 'Handball';
    if (/hockey|ice|dress/i.test(l)) return 'Ice hockey';   // 'Dress' = changing before hockey
    if (/soccer|football|fotboll/i.test(l)) return 'Soccer';
    if (b.kind === 'games' || /game/i.test(l)) return 'Games';
    return 'Other';
  }
  const SPORT_ORDER = ['Handball', 'Ice hockey', 'Soccer', 'Games', 'Other'];
  const EVENT_KINDS = [
    { value: 'game', label: 'Game' }, { value: 'tournament', label: 'Tournament' },
    { value: 'cancellation', label: 'Cancelled' }, { value: 'travel', label: 'Travel' },
  ];
  const EVENT_PILL = {
    game: { color: 'var(--signal)', bg: 'var(--signal-tint)', label: 'game' },
    tournament: { color: 'var(--signal)', bg: 'var(--signal-tint)', label: 'tournament' },
    cancellation: { color: 'var(--warning)', bg: 'var(--warning-tint)', label: 'cancelled' },
    travel: { color: 'var(--fg-muted)', bg: 'var(--ink-50)', label: 'travel' },
  };

  function SportsTab({ memberDb, write, isAdmin }) {
    const L = useLoad(() => A().getSports(memberDb), [memberDb]);
    const W = useWrite(L.reload);
    const [open, setOpen] = useState(false);
    const blank = { kind: 'game', starts_on: A().today(), ends_on: '', start_time: '', end_time: '', label: '', note: '' };
    const [f, setF] = useState(blank);
    const set = (k) => (v) => setF(x => ({ ...x, [k]: v }));
    const canAdd = write && A().canAddEvent(memberDb);   // a boy adds his own games and cancellations (023)

    const blocks = (L.data && L.data.blocks) || [];
    const events = (L.data && L.data.events) || [];
    const groups = SPORT_ORDER.map(s => ({ sport: s, blocks: blocks.filter(b => sportOf(b) === s) })).filter(g => g.blocks.length);
    const seasonEnd = A().SEASON.end;

    async function save() {
      const ev = {
        kind: f.kind, starts_on: f.starts_on, ends_on: f.ends_on && f.ends_on >= f.starts_on ? f.ends_on : f.starts_on,
        start_time: f.start_time || null, end_time: f.end_time || null, label: f.label.trim(), note: f.note.trim() || null,
      };
      const ok = await W.run(() => A().addEvent(memberDb, ev));
      if (ok) { setF(blank); setOpen(false); }
    }
    const ready = f.starts_on && f.label.trim() && !W.busy;

    return (
      <>
        <Section eyebrow="Sports" title="Practice every week">
          <ErrorLine>{L.error}</ErrorLine>
          {L.loading && !L.data ? <Loading /> : groups.length === 0 ? <Empty>No team practice in the plan yet.</Empty> : (
            groups.map(g => {
              const ends = g.blocks.reduce((m, b) => (b.valid_to < m ? b.valid_to : m), seasonEnd);
              return (
                <Card key={g.sport} pad={0} style={{ marginBottom: 10 }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, padding: '10px 12px 6px' }}>
                    <Eyebrow>{g.sport}</Eyebrow>
                    {ends < seasonEnd && <span style={{ fontSize: 11, color: 'var(--fg-subtle)' }}>season ends {fmtLongDate(ends)}</span>}
                  </div>
                  {g.blocks.map(b => (
                    <div key={b.id} style={{ display: 'flex', gap: 10, padding: '8px 12px', borderTop: '1px solid var(--border)', fontSize: 14, alignItems: 'baseline' }}>
                      <span style={{ width: 36, flexShrink: 0, fontWeight: 500 }}>{WD_SHORT[b.weekday]}</span>
                      <span style={{ width: 92, flexShrink: 0, fontFamily: 'var(--font-mono)', fontSize: 12, color: 'var(--fg-muted)' }}>
                        {hhmm(b.start_time)}{b.end_time ? `–${hhmm(b.end_time)}` : ''}
                      </span>
                      <span style={{ flex: 1, minWidth: 0, wordBreak: 'break-word', color: b.optional ? 'var(--fg-muted)' : 'var(--fg)' }}>
                        {b.label}{b.optional ? ' (optional)' : ''}
                      </span>
                    </div>
                  ))}
                </Card>
              );
            })
          )}
        </Section>

        <Section eyebrow="Sports" title="Games and changes"
                 action={canAdd && <AddToggle open={open} onClick={() => setOpen(!open)} />}>
          <Hint>A game on the weekend moves the speed session; free ice is skipped that day.</Hint>
          {write && !isAdmin && <Hint>Add your own games and cancelled practices here; Mum and Dad see them on the family week.</Hint>}
          {open && canAdd && (
            <Card pad={14} style={{ marginBottom: 12 }}>
              <Field label="What">
                <Segmented options={EVENT_KINDS} value={f.kind} onChange={set('kind')} size="sm" />
              </Field>
              <div style={{ ...grid2, marginTop: 10 }}>
                <Field label="Date"><TextIn type="date" value={f.starts_on} onChange={set('starts_on')} /></Field>
                <Field label="Last day (if more than one)"><TextIn type="date" value={f.ends_on} onChange={set('ends_on')} /></Field>
              </div>
              <div style={{ ...grid2, marginTop: 10 }}>
                <Field label="Starts (optional)"><TextIn type="time" value={f.start_time} onChange={set('start_time')} /></Field>
                <Field label="Ends (optional)"><TextIn type="time" value={f.end_time} onChange={set('end_time')} /></Field>
              </div>
              <Field label="Label" style={{ marginTop: 10 }}>
                <TextIn value={f.label} onChange={set('label')} placeholder="Hockey game vs Malmö, away" />
              </Field>
              <Field label="Note (optional)" style={{ marginTop: 10 }}>
                <TextIn value={f.note} onChange={set('note')} placeholder="Leave 07:30, coach wants skates sharpened" />
              </Field>
              <div style={{ marginTop: 12 }}><Button disabled={!ready} onClick={save}>{W.busy ? 'Saving…' : 'Add to the calendar'}</Button></div>
            </Card>
          )}
          <ErrorLine>{W.error}</ErrorLine>
          {L.loading && !L.data ? <Loading /> : events.length === 0 ? <Empty>No games or changes coming up.</Empty> : (
            events.map(e => {
              const p = EVENT_PILL[e.kind] || EVENT_PILL.travel;
              const multi = e.ends_on && e.ends_on !== e.starts_on;
              return (
                <div key={e.id} style={{
                  padding: '10px 12px', marginBottom: 6, border: '1px solid var(--border)', borderRadius: 8,
                  background: 'var(--bg-raised)', borderLeft: `3px solid ${p.color}`,
                }}>
                  <div style={{ display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
                    <Pill color={p.color} bg={p.bg}>{p.label}</Pill>
                    <span style={{ fontSize: 13, fontFamily: 'var(--font-mono)' }}>
                      {fmtDate(e.starts_on)}{multi ? ` – ${fmtDate(e.ends_on)}` : ''}
                      {e.start_time ? ` · ${hhmm(e.start_time)}${e.end_time ? '–' + hhmm(e.end_time) : ''}` : ''}
                    </span>
                    <span style={{ fontSize: 11, color: 'var(--fg-subtle)' }}>{relDay(e.starts_on)}</span>
                  </div>
                  <div style={{ fontSize: 15, marginTop: 4, wordBreak: 'break-word' }}>{e.label}</div>
                  {e.note && <div style={{ fontSize: 13, color: 'var(--fg-muted)', marginTop: 2, wordBreak: 'break-word' }}>{e.note}</div>}
                </div>
              );
            })
          )}
        </Section>
      </>
    );
  }

  // ================================================================= ACTIVITIES
  const CATEGORIES = ['sport', 'leadership', 'service', 'interest', 'work', 'other'];
  const LEVELS = {
    1: 'National or international',
    2: 'Regional or major leadership',
    3: 'School leadership',
    4: 'Taking part',
  };
  const cap = (s) => (s ? s[0].toUpperCase() + s.slice(1) : '');
  const num = (v) => { if (v === '' || v == null) return null; const n = Number(String(v).replace(',', '.')); return Number.isFinite(n) ? n : null; };

  function ActivitiesTab({ memberDb, write }) {
    const L = useLoad(async () => {
      const [acts, logs] = await Promise.all([A().activities.list(memberDb), A().activities.logs(memberDb)]);
      return { acts, logs };
    }, [memberDb]);
    const W = useWrite(L.reload);
    const [open, setOpen] = useState(false);
    const [logFor, setLogFor] = useState(null);
    const blank = { name: '', category: 'sport', role: '', level: '4', started_on: '', hours_per_week: '', weeks_per_year: '', note: '' };
    const [f, setF] = useState(blank);
    const set = (k) => (v) => setF(x => ({ ...x, [k]: v }));

    const acts = (L.data && L.data.acts) || [];
    const logs = (L.data && L.data.logs) || [];
    const byId = Object.fromEntries(acts.map(a => [a.id, a]));
    const active = acts.filter(a => a.active !== false);
    const ended = acts.filter(a => a.active === false);

    async function save() {
      const ok = await W.run(() => A().activities.add(memberDb, {
        name: f.name.trim(), category: f.category, role: f.role.trim() || null,
        level: f.level ? Number(f.level) : null, started_on: f.started_on || null,
        hours_per_week: num(f.hours_per_week), weeks_per_year: num(f.weeks_per_year), note: f.note.trim() || null,
      }));
      if (ok) { setF(blank); setOpen(false); }
    }
    const ready = f.name.trim() && !W.busy;

    return (
      <>
        <Section eyebrow="Activities" title="What I do"
                 action={write && <AddToggle open={open} onClick={() => setOpen(!open)} label="Add activity" />}>
          <Hint>This becomes the activity list for college applications: what, since when, how many hours, what role, how far it went.</Hint>
          {open && write && (
            <Card pad={14} style={{ marginBottom: 12 }}>
              <Field label="Name"><TextIn value={f.name} onChange={set('name')} placeholder="e.g. Youth leader, handball school" /></Field>
              <div style={{ ...grid2, marginTop: 10 }}>
                <Field label="Category">
                  <Select value={f.category} onChange={set('category')} options={CATEGORIES.map(c => ({ value: c, label: cap(c) }))} />
                </Field>
                <Field label="Role"><TextIn value={f.role} onChange={set('role')} placeholder="Player, captain…" /></Field>
              </div>
              <Field label="Level reached" style={{ marginTop: 10 }}>
                <Select value={f.level} onChange={set('level')}
                        options={[{ value: '', label: 'Not set' }, ...[1, 2, 3, 4].map(n => ({ value: String(n), label: `${n} · ${LEVELS[n]}` }))]} />
              </Field>
              <Field label="Started" style={{ marginTop: 10 }}><TextIn type="date" value={f.started_on} onChange={set('started_on')} /></Field>
              <div style={{ ...grid2, marginTop: 10 }}>
                <Field label="Hours per week"><TextIn type="number" inputMode="decimal" value={f.hours_per_week} onChange={set('hours_per_week')} placeholder="3" /></Field>
                <Field label="Weeks per year"><TextIn type="number" inputMode="numeric" value={f.weeks_per_year} onChange={set('weeks_per_year')} placeholder="40" /></Field>
              </div>
              <Field label="Note (optional)" style={{ marginTop: 10 }}><TextIn value={f.note} onChange={set('note')} /></Field>
              <div style={{ marginTop: 12 }}><Button disabled={!ready} onClick={save}>{W.busy ? 'Saving…' : 'Add activity'}</Button></div>
            </Card>
          )}
          <ErrorLine>{L.error || W.error}</ErrorLine>
          {L.loading && !L.data ? <Loading /> : active.length === 0 ? <Empty>No activities yet. Sports count, and so does helping out, leading, working or a hobby.</Empty> : (
            active.map(a => (
              <ActivityCard key={a.id} a={a} write={write} busy={W.busy} logging={logFor === a.id}
                            onLogToggle={() => setLogFor(logFor === a.id ? null : a.id)}
                            onLog={async (r) => { const ok = await W.run(() => A().activities.log(memberDb, { activity_id: a.id, ...r })); if (ok) setLogFor(null); }}
                            recent={logs.filter(l => l.activity_id === a.id).length} />
            ))
          )}
          {ended.length > 0 && (
            <div style={{ marginTop: 10 }}>
              <Eyebrow style={{ marginBottom: 6 }}>Earlier</Eyebrow>
              {ended.map(a => <ActivityCard key={a.id} a={a} dim />)}
            </div>
          )}
        </Section>

        <Section eyebrow="Activities" title="Recent sessions">
          {L.loading && !L.data ? <Loading /> : logs.length === 0 ? <Empty>No sessions logged yet. Use “Log a session” on an activity.</Empty> : (
            <Card pad={0}>
              {logs.slice(0, 10).map((l, n) => (
                <div key={l.id} style={{ display: 'flex', gap: 10, alignItems: 'baseline', padding: '9px 12px', borderTop: n ? '1px solid var(--border)' : 'none', fontSize: 14 }}>
                  <span style={{ width: 84, flexShrink: 0, fontFamily: 'var(--font-mono)', fontSize: 12, color: 'var(--fg-muted)' }}>{fmtDate(l.day)}</span>
                  <span style={{ flex: 1, minWidth: 0, wordBreak: 'break-word' }}>
                    {(byId[l.activity_id] || {}).name || 'Activity'}
                    {l.note && <span style={{ color: 'var(--fg-muted)' }}> · {l.note}</span>}
                  </span>
                  <span style={{ fontFamily: 'var(--font-mono)', fontSize: 12, color: 'var(--fg-muted)', flexShrink: 0 }}>{l.minutes != null ? `${l.minutes} min` : ''}</span>
                </div>
              ))}
            </Card>
          )}
        </Section>

        <section style={{ marginTop: 28 }}>
          <Card pad={18} style={{ background: 'var(--ink-25)' }}>
            <Eyebrow>Something new</Eyebrow>
            <div style={{ fontSize: 17, fontWeight: 500, marginTop: 8 }}>Anything you’d like to try?</div>
            <div style={{ fontSize: 14, color: 'var(--fg-muted)', marginTop: 6, lineHeight: 1.5 }}>
              A non-sport interest is found, not assigned. Music, building, coding, drawing, cooking, animals: try a few things, keep what you like, and add it here when it sticks.
            </div>
          </Card>
        </section>
      </>
    );
  }

  function ActivityCard({ a, dim, write, busy, logging, onLogToggle, onLog, recent }) {
    const [r, setR] = useState({ day: A().today(), minutes: '', note: '' });
    const set = (k) => (v) => setR(x => ({ ...x, [k]: v }));
    const facts = [
      a.role && ['Role', a.role],
      a.started_on && ['Started', fmtLongDate(a.started_on)],
      a.hours_per_week != null && ['Hours / week', a.hours_per_week],
      a.weeks_per_year != null && ['Weeks / year', a.weeks_per_year],
    ].filter(Boolean);
    const mins = num(r.minutes);
    return (
      <Card pad={14} style={{ marginBottom: 10, opacity: dim ? 0.7 : 1 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', gap: 10, alignItems: 'flex-start' }}>
          <div style={{ minWidth: 0 }}>
            <div style={{ fontSize: 16, fontWeight: 500, wordBreak: 'break-word' }}>{a.name}</div>
            <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', marginTop: 6 }}>
              <Pill>{a.category}</Pill>
              {a.level && <Pill color="var(--signal)" bg="var(--signal-tint)">Level {a.level} · {LEVELS[a.level]}</Pill>}
            </div>
          </div>
          {recent > 0 && <span style={{ fontSize: 11, color: 'var(--fg-subtle)', whiteSpace: 'nowrap' }}>{recent} logged</span>}
        </div>
        {facts.length > 0 && (
          <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0,1fr) minmax(0,1fr)', gap: '6px 12px', marginTop: 10 }}>
            {facts.map(([k, v]) => (
              <div key={k} style={{ minWidth: 0 }}>
                <div style={{ fontSize: 11, color: 'var(--fg-subtle)' }}>{k}</div>
                <div style={{ fontSize: 14, wordBreak: 'break-word' }}>{v}</div>
              </div>
            ))}
          </div>
        )}
        {a.note && <div style={{ fontSize: 13, color: 'var(--fg-muted)', marginTop: 8 }}>{a.note}</div>}
        {write && !dim && (
          <div style={{ marginTop: 10 }}>
            {!logging ? <button style={linkBtn} onClick={onLogToggle}>Log a session</button> : (
              <div style={{ borderTop: '1px solid var(--border)', paddingTop: 10 }}>
                <div style={grid2}>
                  <Field label="Date"><TextIn type="date" value={r.day} onChange={set('day')} /></Field>
                  <Field label="Minutes"><TextIn type="number" inputMode="numeric" value={r.minutes} onChange={set('minutes')} placeholder="60" /></Field>
                </div>
                <Field label="Note (optional)" style={{ marginTop: 10 }}><TextIn value={r.note} onChange={set('note')} placeholder="What you did" /></Field>
                <div style={{ display: 'flex', gap: 8, marginTop: 12, alignItems: 'center' }}>
                  <Button size="sm" disabled={!r.day || busy} onClick={() => onLog({ day: r.day, minutes: mins == null ? null : Math.round(mins), note: r.note.trim() || null })}>
                    {busy ? 'Saving…' : 'Save session'}
                  </Button>
                  <Button kind="ghost" size="sm" onClick={onLogToggle}>Cancel</Button>
                </div>
              </div>
            )}
          </div>
        )}
      </Card>
    );
  }

  // ================================================================= screen
  const SUBS = [{ value: 'school', label: 'School' }, { value: 'sports', label: 'Sports' }, { value: 'activities', label: 'Activities' }];

  function LifeScreen({ memberDb, isAdmin, go, sub }) {
    const view = SUBS.some(s => s.value === sub) ? sub : 'school';
    const write = !!memberDb && A().canWrite(memberDb);
    return (
      <div style={{ maxWidth: 720, margin: '0 auto', padding: '20px 16px 120px', fontFamily: 'var(--font-sans)', boxSizing: 'border-box', overflowX: 'hidden' }}>
        <div style={{ display: 'flex', justifyContent: 'center' }}>
          <Segmented options={SUBS} value={view} onChange={(x) => go('life', x)} />
        </div>
        {!memberDb ? <Empty>No one selected.</Empty>
          : view === 'school' ? <SchoolTab key={memberDb} memberDb={memberDb} write={write} />
          : view === 'sports' ? <SportsTab key={memberDb} memberDb={memberDb} write={write} isAdmin={!!isAdmin} />
          : <ActivitiesTab key={memberDb} memberDb={memberDb} write={write} />}
      </div>
    );
  }

  window.LifeScreen = LifeScreen;
})();
