// admin.jsx — v2 group admin: メンバー / 審査問題(並べ替え+一括保存+未保存ガード) / 設定.
(function () {
  const { useState, useEffect, useRef } = React;
  const { Icon } = window;

  function AdminScreen({ app }) {
    const T = app.theme; const [tab, setTab] = useState('members'); const base = `/groups/${app.gid}`;
    const dirtyRef = useRef(false);
    const setDirty = (v) => { dirtyRef.current = v; };
    const confirmLeave = () => !dirtyRef.current || confirm('未保存の変更があります。破棄してよいですか？');

    // warn on browser close/refresh while dirty
    useEffect(() => {
      const h = (e) => { if (dirtyRef.current) { e.preventDefault(); e.returnValue = ''; } };
      window.addEventListener('beforeunload', h);
      return () => window.removeEventListener('beforeunload', h);
    }, []);

    const switchTab = (k) => { if (k === tab) return; if (!confirmLeave()) return; dirtyRef.current = false; setTab(k); };
    const close = () => { if (!confirmLeave()) return; dirtyRef.current = false; app.closeAdmin(); };

    return (
      <div style={{ position: 'absolute', inset: 0, background: T.bg, zIndex: 86, display: 'flex', flexDirection: 'column' }}>
        <div style={{ paddingTop: 54, padding: 'max(54px, env(safe-area-inset-top)) 14px 10px', borderBottom: `1px solid ${T.border}`, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
          <button onClick={close} style={{ background: 'none', border: 'none', cursor: 'pointer', color: T.text }}><Icon.close size={24} /></button>
          <span style={{ fontWeight: 800, color: T.text }}>管理 · {app.copy.appName}</span><span style={{ width: 32 }} />
        </div>
        <div style={{ display: 'flex', gap: 8, padding: '12px 16px 0' }}>
          {[['members', 'メンバー'], ['exam', '審査問題'], ['settings', '設定']].map(([k, l]) => (
            <button key={k} onClick={() => switchTab(k)} style={{ flex: 1, padding: '9px', borderRadius: 10, border: `1px solid ${tab === k ? T.accent : T.border}`, background: tab === k ? T.surface2 : 'none', color: tab === k ? T.text : T.sub, fontWeight: 800, fontSize: 13, cursor: 'pointer', fontFamily: 'inherit' }}>{l}</button>
          ))}
        </div>
        <div style={{ flex: 1, overflow: 'auto', padding: '14px 16px 50px' }}>
          {tab === 'members' ? <Members app={app} base={base} /> : tab === 'exam' ? <ExamEditor app={app} base={base} setDirty={setDirty} /> : <Settings app={app} base={base} setDirty={setDirty} />}
        </div>
      </div>
    );
  }

  function Members({ app, base }) {
    const T = app.theme; const [members, setMembers] = useState(null);
    const load = () => window.PFApi.get(`${base}/members`).then((r) => setMembers(r.members));
    useEffect(() => { load(); }, []);
    if (!members) return <div style={{ color: T.faint }}>読み込み中…</div>;
    const kick = async (m) => { if (!confirm(`${m.name} を追放しますか？`)) return; try { await window.PFApi.post(`${base}/members/${m.id}/remove`, {}); load(); } catch (e) { alert(e.message === 'last_admin' ? '最後の管理者は外せません' : 'エラー'); } };
    const quota = async (m, d) => { await window.PFApi.post(`${base}/members/${m.id}/invites`, { invites_remaining: Math.max(0, (m.invites_remaining || 0) + d) }); load(); };
    return (
      <div>{members.map((m) => (
        <div key={m.id} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '12px 0', borderBottom: `1px solid ${T.border}` }}>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}><span style={{ fontWeight: 800, color: T.text }}>{m.display_name || m.name}</span>{m.role === 'admin' && <span style={{ color: T.accent }}><Icon.verified size={14} /></span>}{m.passed === 1 && m.role !== 'admin' && <span style={{ fontSize: 10, fontWeight: 800, color: T.accent, border: `1px solid ${T.accent}55`, borderRadius: 4, padding: '0 4px' }}>合格</span>}{m.status === 'removed' && <span style={{ fontSize: 10, fontWeight: 800, color: '#c0392b' }}>追放済</span>}</div>
            <div style={{ color: T.faint, fontSize: 12.5 }}>@{m.handle}{m.inviter_name ? ` · ${m.inviter_name}の紹介` : ''}</div>
            <div style={{ color: T.sub, fontSize: 12, marginTop: 3, display: 'flex', alignItems: 'center', gap: 6 }}>招待枠 {m.invites_remaining}{m.role !== 'admin' && <><button onClick={() => quota(m, -1)} style={qb(T)}>−</button><button onClick={() => quota(m, 1)} style={qb(T)}>＋</button></>}</div>
          </div>
          {m.role !== 'admin' && m.status === 'active' && <button onClick={() => kick(m)} style={{ border: `1px solid ${T.border}`, background: 'none', color: '#c0392b', borderRadius: 10, padding: '7px 12px', fontWeight: 800, fontSize: 12.5, cursor: 'pointer', fontFamily: 'inherit', flexShrink: 0 }}>追放</button>}
        </div>))}</div>
    );
  }

  // editable question shape: {id?(real), tmp?(local), question, choices, answer, why}
  function ExamEditor({ app, base, setDirty }) {
    const T = app.theme;
    const draftKey = 'pf_exam_draft_' + app.gid;
    const [qs, setQs] = useState(null);
    const [pass, setPass] = useState(4);
    const [deleted, setDeleted] = useState([]);
    const [dirty, setDirtyState] = useState(false);
    const [saving, setSaving] = useState(false);
    const [restored, setRestored] = useState(false);
    const mark = () => { setDirtyState(true); setDirty(true); };

    const fromServer = (d) => { setQs(d.questions.map((q) => ({ id: q.id, question: q.question, choices: q.choices, answer: q.answer, why: q.why || '' }))); setPass(d.pass_needed); setDeleted([]); setDirtyState(false); setDirty(false); setRestored(false); };
    const load = async () => {
      const d = await window.PFApi.get(`${base}/admin/exam`);
      let draft = null; try { draft = JSON.parse(localStorage.getItem(draftKey) || 'null'); } catch {}
      if (draft && Array.isArray(draft.qs)) { setQs(draft.qs); setPass(draft.pass ?? d.pass_needed); setDeleted(draft.deleted || []); setDirtyState(true); setDirty(true); setRestored(true); }
      else fromServer(d);
    };
    const discardDraft = async () => { localStorage.removeItem(draftKey); fromServer(await window.PFApi.get(`${base}/admin/exam`)); };
    useEffect(() => { load(); }, []);
    // autosave draft so edits survive reload / app-switch / OS back (no data loss)
    useEffect(() => { if (qs && dirty) { try { localStorage.setItem(draftKey, JSON.stringify({ qs, pass, deleted })); } catch {} } }, [qs, pass, deleted, dirty]);
    if (!qs) return <div style={{ color: T.faint }}>読み込み中…</div>;

    const upd = (i, patch) => { setQs((a) => a.map((q, j) => j === i ? { ...q, ...patch } : q)); mark(); };
    const move = (i, dir) => { const j = i + dir; if (j < 0 || j >= qs.length) return; setQs((a) => { const n = [...a]; [n[i], n[j]] = [n[j], n[i]]; return n; }); mark(); };
    const add = () => { setQs((a) => [...a, { tmp: 't' + Date.now(), question: '新しい問題', choices: ['選択肢1', '選択肢2'], answer: 0, why: '' }]); mark(); };
    const del = (i) => { const q = qs[i]; if (!confirm('この問題を削除しますか？')) return; if (q.id) setDeleted((d) => [...d, q.id]); setQs((a) => a.filter((_, j) => j !== i)); mark(); };

    const save = async () => {
      setSaving(true);
      try {
        for (const id of deleted) await window.PFApi.del(`${base}/admin/exam/questions/${id}`);
        for (let i = 0; i < qs.length; i++) {
          const q = qs[i]; const body = { position: i + 1, question: q.question, choices: q.choices, answer: q.answer, why: q.why, active: true };
          if (q.id) await window.PFApi.put(`${base}/admin/exam/questions/${q.id}`, body);
          else await window.PFApi.post(`${base}/admin/exam/questions`, body);
        }
        await window.PFApi.put(`${base}/settings`, { pass_needed: Math.min(pass, qs.length) || 1 });
        localStorage.removeItem(draftKey);
        await load();
        app.toast && app.toast('審査を保存しました');
      } finally { setSaving(false); }
    };

    return (
      <div>
        {restored && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '10px 12px', marginBottom: 12, borderRadius: 10, background: `${T.accent}18`, border: `1px solid ${T.accent}55` }}>
            <span style={{ flex: 1, color: T.text, fontSize: 12.5 }}>未保存の下書きを復元しました（「変更を保存」で確定）</span>
            <button onClick={discardDraft} style={{ border: `1px solid ${T.border}`, background: T.surface, color: T.sub, borderRadius: 8, padding: '5px 10px', fontSize: 12, fontWeight: 700, cursor: 'pointer', fontFamily: 'inherit' }}>下書きを破棄</button>
          </div>
        )}
        <div style={{ marginBottom: 16 }}>
          <div style={{ color: T.sub, fontSize: 13.5, marginBottom: 7 }}>合格に必要な正解数 <span style={{ color: T.faint, fontSize: 12 }}>（全{qs.length}問・タップで選択）</span></div>
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
            {Array.from({ length: qs.length }, (_, i) => i + 1).map((n) => {
              const on = Math.min(pass, qs.length) === n;
              return <button key={n} onClick={() => { setPass(n); mark(); }} aria-pressed={on} style={{ width: 40, height: 40, borderRadius: 10, border: `1.5px solid ${on ? T.accent : T.border}`, background: on ? `${T.accent}1a` : T.surface, color: on ? T.accent : T.text, fontWeight: 800, fontSize: 15, cursor: 'pointer', fontFamily: 'inherit' }}>{n}</button>;
            })}
          </div>
        </div>
        {qs.map((q, i) => (
          <div key={q.id || q.tmp} style={{ border: `1px solid ${T.border}`, borderRadius: 14, padding: 12, marginBottom: 12, background: T.surface }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
              <span style={{ color: T.faint, fontSize: 11.5, fontWeight: 800 }}>Q{i + 1}</span>
              <div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
                <button onClick={() => move(i, -1)} disabled={i === 0} style={{ ...mvb(T), opacity: i === 0 ? 0.3 : 1 }} title="上へ">↑</button>
                <button onClick={() => move(i, 1)} disabled={i === qs.length - 1} style={{ ...mvb(T), opacity: i === qs.length - 1 ? 0.3 : 1 }} title="下へ">↓</button>
                <button onClick={() => del(i)} style={{ border: 'none', background: 'none', color: '#c0392b', cursor: 'pointer', fontSize: 12, marginLeft: 4 }}>削除</button>
              </div>
            </div>
            <textarea value={q.question} onChange={(e) => upd(i, { question: e.target.value })} rows={2} style={{ ...inp(T), resize: 'vertical' }} />
            <div style={{ marginTop: 8, display: 'flex', flexDirection: 'column', gap: 6 }}>
              {q.choices.map((c, ci) => (
                <div key={ci} style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                  <input type="radio" checked={q.answer === ci} onChange={() => upd(i, { answer: ci })} title="正解" />
                  <input value={c} onChange={(e) => upd(i, { choices: q.choices.map((x, k) => k === ci ? e.target.value : x) })} style={{ ...inp(T), flex: 1 }} />
                  <button onClick={() => upd(i, { choices: q.choices.filter((_, k) => k !== ci), answer: q.answer >= ci && q.answer > 0 ? q.answer - 1 : q.answer })} style={{ border: 'none', background: 'none', color: T.faint, cursor: 'pointer' }}>×</button>
                </div>
              ))}
              <button onClick={() => upd(i, { choices: [...q.choices, '選択肢'] })} style={{ alignSelf: 'flex-start', border: 'none', background: 'none', color: T.accent, cursor: 'pointer', fontSize: 12.5, fontWeight: 700 }}>＋ 選択肢</button>
            </div>
            <input value={q.why} onChange={(e) => upd(i, { why: e.target.value })} placeholder="解説（回答後に表示）" style={{ ...inp(T), marginTop: 8 }} />
          </div>
        ))}
        <button onClick={add} style={{ width: '100%', marginTop: 4, padding: '12px', borderRadius: 12, border: `1.5px dashed ${T.border}`, background: 'none', color: T.sub, fontWeight: 700, cursor: 'pointer', fontFamily: 'inherit' }}>＋ 問題を追加</button>
        <button onClick={save} disabled={!dirty || saving} style={{ width: '100%', marginTop: 14, padding: '14px', borderRadius: 14, border: 'none', background: dirty ? T.accent : T.surface2, color: dirty ? '#fff' : T.faint, fontWeight: 800, fontSize: 15, cursor: dirty ? 'pointer' : 'default', fontFamily: 'inherit' }}>{saving ? '保存中…' : dirty ? '変更を保存' : '保存済み'}</button>
      </div>
    );
  }

  function Settings({ app, base, setDirty }) {
    const T = app.theme; const g = app.group || {};
    const init = { name: g.name || '', persona: g.persona_name || '', examT: g.exam_title || '', tagline: g.tagline || '', accent: g.accent || '#d8442f', font: g.font || 'zen' };
    const [s, setS] = useState(init);
    const [saved, setSaved] = useState(false);
    const dirty = JSON.stringify(s) !== JSON.stringify(init);
    useEffect(() => { setDirty(dirty); }, [dirty]);
    const set = (patch) => { setS((x) => ({ ...x, ...patch })); setSaved(false); };
    const save = async () => { await window.PFApi.put(`${base}/settings`, { name: s.name, persona_name: s.persona, exam_title: s.examT, tagline: s.tagline, accent: s.accent, font: s.font }); setSaved(true); setDirty(false); setTimeout(() => location.reload(), 500); };
    const fld = { width: '100%', boxSizing: 'border-box', marginTop: 6, padding: '11px 12px', borderRadius: 10, border: `1px solid ${T.border}`, background: T.surface, color: T.text, fontFamily: 'inherit', fontSize: 14 };
    const lbl = { color: T.faint, fontSize: 12.5, fontWeight: 700, marginTop: 14 };
    const ACCENTS = ['#d8442f', '#1f897a', '#d99a1c', '#2a4a86', '#8b5cf6', '#e0795a', '#5a8f3a'];
    return (
      <div>
        <div style={lbl}>グループ名</div><input value={s.name} onChange={(e) => set({ name: e.target.value })} style={fld} />
        <div style={lbl}>審査の主（persona）</div><input value={s.persona} onChange={(e) => set({ persona: e.target.value })} style={fld} />
        <div style={lbl}>審査タイトル</div><input value={s.examT} onChange={(e) => set({ examT: e.target.value })} style={fld} />
        <div style={lbl}>タグライン（任意）</div><input value={s.tagline} onChange={(e) => set({ tagline: e.target.value })} style={fld} />
        <div style={lbl}>テーマカラー</div>
        <div style={{ display: 'flex', gap: 8, marginTop: 6, flexWrap: 'wrap' }}>
          {ACCENTS.map((a) => <button key={a} onClick={() => set({ accent: a })} style={{ width: 34, height: 34, borderRadius: 9, background: a, border: s.accent === a ? `2px solid ${T.text}` : '2px solid transparent', cursor: 'pointer' }} />)}
          <label style={{ width: 34, height: 34, borderRadius: 9, border: `1px dashed ${T.border}`, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', overflow: 'hidden' }}><input type="color" value={s.accent} onChange={(e) => set({ accent: e.target.value })} style={{ width: 44, height: 44, border: 'none', padding: 0, cursor: 'pointer' }} /></label>
        </div>
        <div style={lbl}>フォント（グループ標準）</div>
        <div style={{ display: 'flex', gap: 6, marginTop: 6, flexWrap: 'wrap' }}>
          {Object.entries(window.PF_FONTS).map(([k, f]) => { const on = s.font === k; return <button key={k} onClick={() => set({ font: k })} style={{ padding: '7px 12px', borderRadius: 999, border: `1px solid ${on ? T.accent : T.border}`, background: on ? `${T.accent}1a` : T.surface, color: T.text, fontFamily: f.css, fontSize: 13, fontWeight: 700, cursor: 'pointer' }}>{f.label}</button>; })}
        </div>
        <div style={{ color: T.faint, fontSize: 11, marginTop: 6 }}>※各メンバーは「あなた→見た目」で自分用に上書きできます。</div>
        <button onClick={save} disabled={!dirty} style={{ width: '100%', marginTop: 22, padding: '14px', borderRadius: 14, border: 'none', background: dirty ? T.accent : T.surface2, color: dirty ? '#fff' : T.faint, fontWeight: 800, fontSize: 15, cursor: dirty ? 'pointer' : 'default', fontFamily: 'inherit' }}>{saved ? '保存しました' : dirty ? '設定を保存' : '保存済み'}</button>
      </div>
    );
  }

  const qb = (T) => ({ width: 22, height: 22, borderRadius: 6, border: `1px solid ${T.border}`, background: T.surface, color: T.text, cursor: 'pointer', fontFamily: 'inherit', lineHeight: 1 });
  const mvb = (T) => ({ width: 28, height: 24, borderRadius: 6, border: `1px solid ${T.border}`, background: T.surface, color: T.text, cursor: 'pointer', fontFamily: 'inherit', fontSize: 13, lineHeight: 1 });
  const inp = (T) => ({ width: '100%', boxSizing: 'border-box', padding: '8px 10px', borderRadius: 8, border: `1px solid ${T.border}`, background: T.surface, color: T.text, fontFamily: 'inherit', fontSize: 13.5 });

  window.AdminScreen = AdminScreen;
})();
