// Admin Settings — Phases + Milestones + Team member listing
// Accessible to admin role only. Read-only view for others.

function AdminSettings({ onClose, perms, phases, milestones, team, lang, t, reload, workstreams, onWorkstreamsChange }) {
  const canEdit = !!perms?.canEdit && (perms?.role === 'admin');
  const canEditWs = !!perms?.canEdit && (perms?.role === 'admin' || perms?.role === 'strategy');
  const [tab, setTab] = useState('phases'); // 'phases' | 'milestones' | 'team' | 'workstreams'
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState('');

  const L = {
    ko: {
      title: '관리자 설정',
      tabPhases: '프로젝트 단계',
      tabMilestones: '마일스톤',
      tabTeam: '팀 멤버',
      tabWorkstreams: '워크스트림',
      close: '닫기',
      addMilestone: '+ 마일스톤 추가',
      addMember: '+ 팀원 추가',
      deleteConfirm: '이 마일스톤을 삭제하시겠습니까?',
      removeMemberConfirm: '명단에서 삭제하시겠습니까?',
      removeJoinedConfirm: '이미 가입한 사용자입니다. 정말 명단에서 삭제할까요? (profiles·auth 는 유지됩니다)',
      readOnly: '읽기 전용 (admin만 수정 가능)',
      name: '이름', code: '코드', label: '표시명', start: '시작일', end: '종료일',
      target: '목표일', icon: '아이콘', color: '색상', desc: '설명', order: '순서',
      email: '이메일', fullName: '이름(KR)', englishName: 'Name(EN)', title2: '직책',
      role: '권한', teamCol: '팀', joined: '가입', notes: '메모',
      newMilestone: '새 마일스톤',
      saving: '저장 중…',
      saveErr: '저장 실패',
      empty: '등록된 항목이 없습니다',
      emailRequired: '이메일은 필수입니다',
      nameRequired: '이름(KR)은 필수입니다',
      labelEmpty: '표시명은 비어있을 수 없습니다',
    },
    en: {
      title: 'Admin Settings',
      tabPhases: 'Phases',
      tabMilestones: 'Milestones',
      tabTeam: 'Team',
      tabWorkstreams: 'Workstreams',
      close: 'Close',
      addMilestone: '+ Add milestone',
      addMember: '+ Add member',
      deleteConfirm: 'Delete this milestone?',
      removeMemberConfirm: 'Remove from roster?',
      removeJoinedConfirm: 'This user has already joined. Really remove from roster? (profiles/auth are kept)',
      readOnly: 'Read-only (admins only)',
      name: 'Name', code: 'Code', label: 'Label', start: 'Start', end: 'End',
      target: 'Target date', icon: 'Icon', color: 'Color', desc: 'Description', order: 'Order',
      email: 'Email', fullName: 'Full name', englishName: 'Name (EN)', title2: 'Title',
      role: 'Role', teamCol: 'Team', joined: 'Joined', notes: 'Notes',
      newMilestone: 'New milestone',
      saving: 'Saving…',
      saveErr: 'Save failed',
      empty: 'No items yet',
      emailRequired: 'Email is required',
      nameRequired: 'Full name is required',
      labelEmpty: 'Label cannot be empty',
    }
  };
  const LL = L[lang] || L.ko;

  // Helpers ----------------------------------------------------------------
  async function savePhase(code, patch) {
    if (!canEdit) return;
    // Label must be non-empty (NOT NULL in DB). Block the write client-side.
    if (patch.label !== undefined && !String(patch.label).trim()) {
      setErr(lang === 'ko' ? '표시명은 비어있을 수 없습니다' : 'Label cannot be empty');
      return;
    }
    setBusy(true); setErr('');
    try {
      // Merge the partial patch on top of the current phase row so upsert has
      // every NOT-NULL column (label, start_date, end_date) — otherwise
      // editing only `end_date` on a row that was never persisted tries to
      // INSERT { phase_code, end_date } and fails the label NOT NULL check.
      const current = (phases || []).find(p => p.phase_code === code) || {};
      const merged = {
        label:      current.label      ?? '',
        start_date: current.start_date ?? null,
        end_date:   current.end_date   ?? null,
        sort_order: current.sort_order,
        ...patch,
      };
      await window.AOX_DB.updatePhaseConfig(code, merged);
      if (reload) await reload();
    }
    catch(e) {
      console.error('[admin] phase save error', e);
      // Suppress schema-cache errors for optional columns (e.g. color before migration runs)
      if (/column.*schema cache/i.test(e.message || '')) {
        console.warn('[admin] phase_config column missing — run migration Q5b first');
        setErr(lang === 'ko'
          ? 'DB 컬럼 미존재 — migrations/2026-04-24_Q5b_phase_config_color.sql 실행 후 재시도'
          : 'DB column missing — run migrations/2026-04-24_Q5b_phase_config_color.sql first');
      } else {
        setErr(e.message || LL.saveErr);
      }
    }
    finally { setBusy(false); }
  }
  async function saveMilestone(id, patch) {
    if (!canEdit) return;
    setBusy(true); setErr('');
    try {
      if (String(id).startsWith('default-')) {
        // Defaults aren't real rows yet — insert instead
        const base = milestones.find(m => m.id === id) || {};
        await window.AOX_DB.insertMilestone({ ...base, ...patch, id: undefined });
      } else {
        await window.AOX_DB.updateMilestone(id, patch);
      }
      if (reload) await reload();
    } catch(e) { console.error('[admin] milestone save error', e); setErr(e.message || LL.saveErr); }
    finally { setBusy(false); }
  }
  async function addMilestone() {
    if (!canEdit) return;
    setBusy(true); setErr('');
    try {
      await window.AOX_DB.insertMilestone({
        name: LL.newMilestone, target_date: new Date().toISOString().slice(0,10),
        icon: '🎯', color: '#6B7280', sort_order: (milestones.length || 0) + 1,
      });
      if (reload) await reload();
    } catch(e) { console.error('[admin] milestone insert error', e); setErr(e.message || LL.saveErr); }
    finally { setBusy(false); }
  }
  async function removeMilestone(id) {
    if (!canEdit) return;
    if (String(id).startsWith('default-')) return; // defaults can't be deleted
    if (!window.confirm(LL.deleteConfirm)) return;
    setBusy(true); setErr('');
    try { await window.AOX_DB.deleteMilestone(id); if (reload) await reload(); }
    catch(e) { console.error('[admin] milestone delete error', e); setErr(e.message || LL.saveErr); }
    finally { setBusy(false); }
  }

  const tabBtn = (active) => ({
    padding:'6px 12px', borderRadius: 8,
    backgroundColor: active ? 'var(--accent-soft)' : 'transparent',
    border:'1px solid ' + (active ? 'color-mix(in srgb, var(--accent) 40%, var(--border))' : 'transparent'),
    fontSize: 12, fontWeight: 600,
    color: active ? 'var(--accent)' : 'var(--fg-muted)',
    cursor:'pointer',
  });

  return (
    <div className="modal-backdrop" onClick={onClose}>
      <div className="modal" onClick={e=>e.stopPropagation()} style={{width:'min(860px, 94vw)', maxHeight:'84vh'}}>
        <header style={{display:'flex', alignItems:'center', gap:10, justifyContent:'space-between'}}>
          <div style={{display:'flex', alignItems:'center', gap:10}}>
            <div style={{
              width: 26, height: 26, borderRadius: 7,
              background:'linear-gradient(135deg, var(--accent), var(--phase2))',
              display:'flex', alignItems:'center', justifyContent:'center', color:'#fff',
              fontSize: 13, fontWeight: 800
            }}>⚙</div>
            <div style={{fontSize: 14, fontWeight: 700, color:'var(--fg)'}}>{LL.title}</div>
            {!canEdit && (
              <span style={{
                marginLeft: 6, padding:'2px 8px', fontSize: 10.5, fontWeight: 600,
                background:'var(--surface-2)', color:'var(--fg-muted)',
                borderRadius: 999, border:'1px solid var(--divider)'
              }}>{LL.readOnly}</span>
            )}
          </div>
          <button className="btn" onClick={onClose}>{LL.close}</button>
        </header>

        {/* Tabs */}
        <div style={{padding:'10px 20px 4px', display:'flex', gap:6, borderBottom:'1px solid var(--divider)'}}>
          <button type="button" onClick={()=>setTab('phases')}       style={tabBtn(tab==='phases')}>{LL.tabPhases}</button>
          <button type="button" onClick={()=>setTab('milestones')}   style={tabBtn(tab==='milestones')}>{LL.tabMilestones}</button>
          <button type="button" onClick={()=>setTab('team')}         style={tabBtn(tab==='team')}>{LL.tabTeam}</button>
          {canEditWs && (
            <button type="button" onClick={()=>setTab('workstreams')} style={tabBtn(tab==='workstreams')}>{LL.tabWorkstreams}</button>
          )}
          <div style={{flex:1}}/>
          {busy && <span style={{fontSize:11, color:'var(--fg-muted)', alignSelf:'center'}}>{LL.saving}</span>}
        </div>

        <div className="body" style={{overflow:'auto'}}>
          {err && (
            <div style={{
              margin:'4px 0 14px', padding:'8px 12px',
              background:'rgba(255,59,48,0.10)', border:'1px solid rgba(255,59,48,0.30)',
              color:'#FF3B30', borderRadius: 8, fontSize: 12
            }}>{err}</div>
          )}

          {tab === 'phases' && <PhasesEditor phases={phases} canEdit={canEdit} onSave={savePhase} LL={LL} onReload={reload}/>}
          {tab === 'milestones' && (
            <MilestonesEditor
              milestones={milestones}
              canEdit={canEdit}
              onSave={saveMilestone}
              onAdd={addMilestone}
              onRemove={removeMilestone}
              LL={LL}
            />
          )}
          {tab === 'team' && <TeamRosterEditor canEdit={canEdit} LL={LL} lang={lang} setErr={setErr}/>}
          {tab === 'workstreams' && canEditWs && (
            <WorkstreamEditor
              workstreams={workstreams || []}
              canEdit={canEditWs}
              lang={lang}
              setErr={setErr}
              onChanged={onWorkstreamsChange}
            />
          )}
        </div>
      </div>
    </div>
  );
}

// --- Phases editor ------------------------------------------------------
function PhasesEditor({ phases, canEdit, onSave, LL, onAdd, onDelete, onReload }) {
  const ROW_H = 44;
  const GRID = '110px 1.4fr 130px 130px 60px 80px 44px';
  const cell = { padding:'0 10px', fontSize: 12.5, display:'flex', alignItems:'center' };
  const [busy, setBusy] = useState(false);
  const [addOpen, setAddOpen] = useState(false);
  const [draft, setDraft] = useState({ phase_code:'', label:'', start_date:'', end_date:'', color:'' });

  const input = (v, onChange, type='text') => (
    <input type={type} defaultValue={v || ''} disabled={!canEdit}
      onBlur={e => { const nv = e.target.value; if (nv !== (v||'')) onChange(nv); }}
      style={{
        width:'100%', height: 28, boxSizing:'border-box',
        background:'transparent', border:'1px solid transparent',
        padding:'0 8px', borderRadius: 6, fontSize: 12.5, color:'var(--fg)',
        fontFamily: type==='date' ? 'var(--font-mono)' : 'inherit',
      }}
      onFocus={e => { e.currentTarget.style.borderColor='var(--accent)'; e.currentTarget.style.background='var(--bg-solid)'; }}
      onMouseLeave={e => { if (document.activeElement !== e.currentTarget) { e.currentTarget.style.borderColor='transparent'; e.currentTarget.style.background='transparent'; } }}
    />
  );

  const phaseColor = (p, i) => {
    if (p.color) return p.color;
    const n = (p.phase_code || '').match(/\d/)?.[0] || (i+1);
    return `var(--phase${n})`;
  };

  async function handleDelete(code) {
    if (!window.confirm(LL.deleteConfirm || `Delete phase "${code}"?`)) return;
    setBusy(true);
    try { await window.AOX_DB.deletePhaseConfig(code); if (onReload) await onReload(); }
    catch(e) { alert(e.message); }
    finally { setBusy(false); }
  }

  async function handleAdd() {
    if (!draft.phase_code.trim() || !draft.label.trim()) return;
    setBusy(true);
    try {
      await window.AOX_DB.insertPhaseConfig({
        phase_code: draft.phase_code.trim(),
        label: draft.label.trim(),
        start_date: draft.start_date || null,
        end_date: draft.end_date || null,
        color: draft.color || null,
        sort_order: (phases.length || 0) + 1,
      });
      setAddOpen(false);
      setDraft({ phase_code:'', label:'', start_date:'', end_date:'', color:'' });
      if (onReload) await onReload();
    }
    catch(e) { alert(e.message); }
    finally { setBusy(false); }
  }

  const iconBtn = { padding:'3px 8px', borderRadius: 6, border:'1px solid var(--divider)', background:'transparent', cursor:'pointer', fontSize: 11, fontWeight:600, color:'var(--fg-muted)' };
  const delBtn  = { ...iconBtn, color:'#FF3B30', borderColor:'rgba(255,59,48,0.4)' };

  return (
    <div>
      <div style={{border:'1px solid var(--divider)', borderRadius: 10, overflow:'hidden'}}>
        <div style={{display:'grid', gridTemplateColumns: GRID, height: 32, alignItems:'center',
            background:'var(--surface-2)', fontSize: 11, fontWeight: 600, color:'var(--fg-muted)', letterSpacing: 0.04,
            borderBottom:'1px solid var(--divider)'}}>
          <div style={{padding:'0 10px'}}>{LL.code}</div>
          <div style={{padding:'0 10px'}}>{LL.label}</div>
          <div style={{padding:'0 10px'}}>{LL.start}</div>
          <div style={{padding:'0 10px'}}>{LL.end}</div>
          <div style={{padding:'0 10px', textAlign:'right'}}>{LL.order}</div>
          <div style={{padding:'0 10px', textAlign:'center'}}>{LL.color}</div>
          <div/>
        </div>
        {phases.map((p, i) => (
          <div key={p.phase_code} style={{display:'grid', gridTemplateColumns: GRID, height: ROW_H,
              alignItems:'center', borderBottom:'1px solid var(--divider)'}}>
            <div style={{...cell, gap: 8, fontWeight: 600}}>
              <span style={{width: 4, height: 14, background: phaseColor(p, i), borderRadius: 2}}/>
              {p.phase_code}
            </div>
            <div style={cell}>{input(p.label, (v) => onSave(p.phase_code, { label: v }))}</div>
            <div style={cell}>{input(p.start_date, (v) => onSave(p.phase_code, { start_date: v }), 'date')}</div>
            <div style={cell}>{input(p.end_date,   (v) => onSave(p.phase_code, { end_date: v }), 'date')}</div>
            <div style={{...cell, justifyContent:'flex-end', color:'var(--fg-muted)', fontFamily:'var(--font-mono)'}}>{p.sort_order}</div>
            <div style={{...cell, justifyContent:'center'}}>
              <input type="color" defaultValue={p.color || '#888888'} disabled={!canEdit}
                onChange={e => onSave(p.phase_code, { color: e.target.value })}
                style={{width: 28, height: 26, boxSizing:'border-box', border:'1px solid var(--border)', borderRadius: 6, background:'transparent', padding: 0, cursor: canEdit ? 'pointer' : 'not-allowed'}}/>
            </div>
            <div style={{...cell, justifyContent:'flex-end', gap: 4, paddingRight: 10}}>
              {canEdit && (
                <button style={delBtn} disabled={busy} onClick={() => handleDelete(p.phase_code)}>×</button>
              )}
            </div>
          </div>
        ))}
        {addOpen && (
          <div style={{display:'grid', gridTemplateColumns: GRID, height: ROW_H, alignItems:'center',
              borderBottom:'1px solid var(--divider)', background:'var(--accent-soft)'}}>
            <div style={{...cell}}>
              <input value={draft.phase_code} onChange={e => setDraft({...draft, phase_code: e.target.value})}
                placeholder="Phase 5"
                style={{width:'100%', height:28, boxSizing:'border-box', padding:'0 8px', borderRadius:6, border:'1px solid var(--accent)', fontSize:12.5, background:'var(--bg-solid)', color:'var(--fg)'}}/>
            </div>
            <div style={{...cell}}>
              <input value={draft.label} onChange={e => setDraft({...draft, label: e.target.value})}
                placeholder="Phase name"
                style={{width:'100%', height:28, boxSizing:'border-box', padding:'0 8px', borderRadius:6, border:'1px solid var(--border)', fontSize:12.5, background:'var(--bg-solid)', color:'var(--fg)'}}/>
            </div>
            <div style={{...cell}}>
              <input type="date" value={draft.start_date} onChange={e => setDraft({...draft, start_date: e.target.value})}
                style={{width:'100%', height:28, boxSizing:'border-box', padding:'0 8px', borderRadius:6, border:'1px solid var(--border)', fontSize:12.5, background:'var(--bg-solid)', color:'var(--fg)', fontFamily:'var(--font-mono)'}}/>
            </div>
            <div style={{...cell}}>
              <input type="date" value={draft.end_date} onChange={e => setDraft({...draft, end_date: e.target.value})}
                style={{width:'100%', height:28, boxSizing:'border-box', padding:'0 8px', borderRadius:6, border:'1px solid var(--border)', fontSize:12.5, background:'var(--bg-solid)', color:'var(--fg)', fontFamily:'var(--font-mono)'}}/>
            </div>
            <div style={{...cell, color:'var(--fg-muted)', justifyContent:'flex-end', fontSize:11}}>{phases.length+1}</div>
            <div style={{...cell, justifyContent:'center'}}>
              <input type="color" value={draft.color || '#888888'} onChange={e => setDraft({...draft, color: e.target.value})}
                style={{width:28, height:26, borderRadius:6, border:'1px solid var(--border)', padding:0, cursor:'pointer'}}/>
            </div>
            <div style={{...cell, gap:4, paddingRight:10, justifyContent:'flex-end'}}>
              <button style={iconBtn} onClick={() => setAddOpen(false)}>✕</button>
              <button style={{...iconBtn, color:'var(--accent)', borderColor:'var(--accent)'}}
                disabled={busy || !draft.phase_code.trim() || !draft.label.trim()}
                onClick={handleAdd}>✓</button>
            </div>
          </div>
        )}
      </div>
      {canEdit && !addOpen && (
        <div style={{marginTop: 12}}>
          <button className="btn btn-primary" onClick={() => setAddOpen(true)}>+ Phase 추가</button>
        </div>
      )}
    </div>
  );
}

// --- Milestones editor --------------------------------------------------
function MilestonesEditor({ milestones, canEdit, onSave, onAdd, onRemove, LL }) {
  const ROW_H = 40;
  const GRID = '62px 1.4fr 130px 60px 70px 50px';
  const cell = { padding:'0 10px', fontSize: 12.5, display:'flex', alignItems:'center' };
  const inp = (v, onChange, type='text', style={}) => (
    <input type={type} defaultValue={v || ''} disabled={!canEdit}
      onBlur={e => { const nv = e.target.value; if (nv !== (v||'')) onChange(nv); }}
      style={{
        width:'100%', height: 28, boxSizing:'border-box',
        background:'transparent', border:'1px solid transparent',
        padding:'0 8px', borderRadius: 6, fontSize: 12.5, color:'var(--fg)', ...style,
      }}
      onFocus={e => { e.currentTarget.style.borderColor='var(--accent)'; e.currentTarget.style.background='var(--bg-solid)'; }}
      onMouseLeave={e => { if (document.activeElement !== e.currentTarget) { e.currentTarget.style.borderColor='transparent'; e.currentTarget.style.background='transparent'; } }}
    />
  );

  return (
    <div>
      <div style={{border:'1px solid var(--divider)', borderRadius: 10, overflow:'hidden'}}>
        <div style={{display:'grid', gridTemplateColumns: GRID, height: 32, alignItems:'center',
            background:'var(--surface-2)', fontSize: 11, fontWeight: 600, color:'var(--fg-muted)', letterSpacing: 0.04,
            borderBottom:'1px solid var(--divider)'}}>
          <div style={{padding:'0 10px', textAlign:'center'}}>{LL.icon}</div>
          <div style={{padding:'0 10px'}}>{LL.name}</div>
          <div style={{padding:'0 10px'}}>{LL.target}</div>
          <div style={{padding:'0 10px'}}>{LL.color}</div>
          <div style={{padding:'0 10px', textAlign:'right'}}>{LL.order}</div>
          <div/>
        </div>
        {milestones.length === 0 ? (
          <div style={{padding:'16px', textAlign:'center', color:'var(--fg-muted)', fontSize: 12}}>{LL.empty}</div>
        ) : milestones.map(m => (
          <div key={m.id} style={{display:'grid', gridTemplateColumns: GRID, height: ROW_H,
              alignItems:'center', borderBottom:'1px solid var(--divider)'}}>
            <div style={{...cell, justifyContent:'center', fontSize: 18}}>
              {inp(m.icon, (v)=>onSave(m.id, {icon: v}), 'text', {textAlign:'center', fontSize: 18})}
            </div>
            <div style={cell}>{inp(m.name, (v)=>onSave(m.id, {name: v}))}</div>
            <div style={cell}>{inp(m.target_date, (v)=>onSave(m.id, {target_date: v}), 'date', {fontFamily:'var(--font-mono)'})}</div>
            <div style={cell}>
              <input type="color" defaultValue={m.color || '#6B7280'} disabled={!canEdit}
                onChange={e=>onSave(m.id, {color: e.target.value})}
                style={{width:'100%', height: 26, boxSizing:'border-box', border:'1px solid var(--border)', borderRadius: 6, background:'transparent', padding: 0, cursor: canEdit ? 'pointer' : 'not-allowed'}}/>
            </div>
            <div style={{...cell, justifyContent:'flex-end'}}>
              {inp(m.sort_order, (v)=>onSave(m.id, {sort_order: parseInt(v,10)||0}), 'number', {textAlign:'right', fontFamily:'var(--font-mono)'})}
            </div>
            <div style={{...cell, justifyContent:'center'}}>
              {canEdit && !String(m.id).startsWith('default-') && (
                <button type="button" onClick={()=>onRemove(m.id)} title="Delete"
                  style={{
                    width: 26, height: 26, padding: 0, borderRadius: 6,
                    background:'transparent', border:'1px solid var(--divider)',
                    color:'var(--fg-muted)', cursor:'pointer', fontSize: 13,
                    display:'inline-flex', alignItems:'center', justifyContent:'center',
                  }}
                  onMouseEnter={e=>{ e.currentTarget.style.color='#FF3B30'; e.currentTarget.style.borderColor='#FF3B30'; }}
                  onMouseLeave={e=>{ e.currentTarget.style.color='var(--fg-muted)'; e.currentTarget.style.borderColor='var(--divider)'; }}>×</button>
              )}
            </div>
          </div>
        ))}
      </div>
      {canEdit && (
        <div style={{marginTop: 12}}>
          <button className="btn btn-primary" onClick={onAdd}>{LL.addMilestone}</button>
        </div>
      )}
    </div>
  );
}

// --- Team roster editor (shared.team_roster) ----------------------------
// CRUD over pre-registered team members. `joined` flips true server-side
// (via a Supabase trigger) once the user signs up and a profiles row exists.
const TEAM_OPTIONS = ['executive','strategy','planning','engineering','bizops','sales','marketing','ops'];
const TEAM_ORDER   = Object.fromEntries(TEAM_OPTIONS.map((t, i) => [t, i]));
const ROLE_OPTIONS = ['admin','strategy','team_member','viewer'];

function TeamRosterEditor({ canEdit, LL, lang, setErr }) {
  const [roster, setRoster] = useState(null); // null = loading, [] = empty
  const [busy, setBusy] = useState(false);
  const [draft, setDraft] = useState(null); // {email, full_name, english_name, title, team, planned_role, notes} or null

  async function reload() {
    try {
      const rows = await window.AOX_DB.fetchTeamRoster();
      setRoster(rows);
    } catch (e) { setErr(e.message || LL.saveErr); setRoster([]); }
  }
  useEffect(() => { reload(); /* eslint-disable-line */ }, []);

  const sorted = useMemo(() => {
    if (!roster) return [];
    return [...roster].sort((a, b) => {
      const ta = TEAM_ORDER[a.team] ?? 99;
      const tb = TEAM_ORDER[b.team] ?? 99;
      if (ta !== tb) return ta - tb;
      return (a.full_name || a.email || '').localeCompare(b.full_name || b.email || '', 'ko');
    });
  }, [roster]);

  async function save(email, patch) {
    if (!canEdit) return;
    setBusy(true); setErr('');
    try { await window.AOX_DB.updateRoster(email, patch); await reload(); }
    catch (e) { setErr(e.message || LL.saveErr); }
    finally { setBusy(false); }
  }

  async function add() {
    if (!canEdit) return;
    if (!draft?.email?.trim()) { setErr(LL.emailRequired); return; }
    if (!draft?.full_name?.trim()) { setErr(LL.nameRequired); return; }
    setBusy(true); setErr('');
    try {
      await window.AOX_DB.insertRoster({
        email: draft.email.trim().toLowerCase(),
        full_name: draft.full_name.trim(),
        english_name: draft.english_name?.trim() || null,
        title: draft.title?.trim() || null,
        team: draft.team || null,
        planned_role: draft.planned_role || 'viewer',
        notes: draft.notes?.trim() || null,
      });
      setDraft(null);
      await reload();
    } catch (e) { setErr(e.message || LL.saveErr); }
    finally { setBusy(false); }
  }

  async function remove(row) {
    if (!canEdit) return;
    if (!window.confirm(LL.removeMemberConfirm + '\n' + (row.email))) return;
    if (row.joined && !window.confirm(LL.removeJoinedConfirm)) return;
    setBusy(true); setErr('');
    try { await window.AOX_DB.deleteRoster(row.email); await reload(); }
    catch (e) { setErr(e.message || LL.saveErr); }
    finally { setBusy(false); }
  }

  const ROW_H = 40;
  const gridTemplate = '1fr 1fr 0.9fr 140px 1.5fr 130px 46px 40px';
  const cell = { padding:'0 10px', fontSize: 12.5, display:'flex', alignItems:'center' };
  const inp = (v, onChange, opts={}) => (
    <input type={opts.type || 'text'} defaultValue={v ?? ''} disabled={!canEdit || opts.readOnly}
      placeholder={opts.placeholder || ''}
      onBlur={e => { const nv = e.target.value; if (nv !== (v ?? '')) onChange(nv); }}
      style={{
        width:'100%', height: 28, boxSizing:'border-box',
        background:'transparent', border:'1px solid transparent',
        padding:'0 8px', borderRadius: 6, fontSize: 12.5, color:'var(--fg)',
        fontFamily: opts.mono ? 'var(--font-mono)' : 'inherit',
        ...(opts.readOnly ? { color: 'var(--fg-muted)', cursor:'default' } : {}),
      }}
      onFocus={e => { if (!opts.readOnly) { e.currentTarget.style.borderColor='var(--accent)'; e.currentTarget.style.background='var(--bg-solid)'; } }}
      onMouseLeave={e => { if (document.activeElement !== e.currentTarget) { e.currentTarget.style.borderColor='transparent'; e.currentTarget.style.background='transparent'; } }}
    />
  );
  const sel = (v, onChange, options, placeholder) => (
    <select defaultValue={v || ''} disabled={!canEdit}
      onChange={e => { if (e.target.value !== (v || '')) onChange(e.target.value); }}
      style={{
        width:'100%', height: 28, boxSizing:'border-box',
        background:'transparent', border:'1px solid transparent',
        padding:'0 4px', borderRadius: 6, fontSize: 12, color:'var(--fg)',
        cursor: canEdit ? 'pointer' : 'default',
        overflow:'hidden', textOverflow:'ellipsis',
      }}>
      {placeholder != null && <option value="">{placeholder}</option>}
      {options.map(o => <option key={o} value={o}>{o}</option>)}
    </select>
  );

  if (roster === null) {
    return <div style={{padding: 20, textAlign:'center', color:'var(--fg-muted)', fontSize: 12.5}}>loading…</div>;
  }

  const rowStyle = {
    display:'grid', gridTemplateColumns: gridTemplate, height: ROW_H,
    alignItems:'center', borderBottom:'1px solid var(--divider)',
  };

  return (
    <div>
      <div style={{border:'1px solid var(--divider)', borderRadius: 10, overflow:'hidden'}}>
        <div style={{display:'grid', gridTemplateColumns: gridTemplate, height: 32, alignItems:'center',
            background:'var(--surface-2)', fontSize: 11, fontWeight: 600, color:'var(--fg-muted)', letterSpacing: 0.04,
            borderBottom:'1px solid var(--divider)'}}>
          <div style={{padding:'0 10px'}}>{LL.fullName}</div>
          <div style={{padding:'0 10px'}}>{LL.englishName}</div>
          <div style={{padding:'0 10px'}}>{LL.title2}</div>
          <div style={{padding:'0 10px'}}>{LL.teamCol}</div>
          <div style={{padding:'0 10px'}}>{LL.email}</div>
          <div style={{padding:'0 10px'}}>{LL.role}</div>
          <div style={{padding:'0 10px', textAlign:'center'}}>{LL.joined}</div>
          <div/>
        </div>
        {sorted.length === 0 && !draft && (
          <div style={{padding:'16px', textAlign:'center', color:'var(--fg-muted)', fontSize: 12}}>{LL.empty}</div>
        )}
        {sorted.map(r => (
          <div key={r.email} style={rowStyle}>
            <div style={cell}>{inp(r.full_name, v => save(r.email, {full_name: v}))}</div>
            <div style={cell}>{inp(r.english_name, v => save(r.email, {english_name: v || null}))}</div>
            <div style={cell}>{inp(r.title, v => save(r.email, {title: v || null}))}</div>
            <div style={cell}>{sel(r.team, v => save(r.email, {team: v || null}), TEAM_OPTIONS, '—')}</div>
            <div style={{...cell, color:'var(--fg-muted)'}}>{inp(r.email, null, {readOnly: true, mono: true})}</div>
            <div style={cell}>{sel(r.planned_role, v => save(r.email, {planned_role: v || null}), ROLE_OPTIONS, '—')}</div>
            <div style={{...cell, justifyContent:'center', fontSize: 13}}>{r.joined ? '✓' : ''}</div>
            <div style={{...cell, justifyContent:'center'}}>
              {canEdit && (
                <button type="button" onClick={()=>remove(r)} title="Delete"
                  style={{
                    width: 26, height: 26, padding: 0, borderRadius: 6,
                    background:'transparent', border:'1px solid var(--divider)',
                    color:'var(--fg-muted)', cursor:'pointer', fontSize: 13,
                    display:'inline-flex', alignItems:'center', justifyContent:'center',
                  }}
                  onMouseEnter={e=>{ e.currentTarget.style.color='#FF3B30'; e.currentTarget.style.borderColor='#FF3B30'; }}
                  onMouseLeave={e=>{ e.currentTarget.style.color='var(--fg-muted)'; e.currentTarget.style.borderColor='var(--divider)'; }}>×</button>
              )}
            </div>
          </div>
        ))}
        {draft && (
          <div style={{...rowStyle, background: 'var(--accent-soft)'}}>
            <div style={cell}>{inp(draft.full_name, v => setDraft({...draft, full_name: v}), {placeholder:'홍길동'})}</div>
            <div style={cell}>{inp(draft.english_name, v => setDraft({...draft, english_name: v}), {placeholder:'Hong'})}</div>
            <div style={cell}>{inp(draft.title, v => setDraft({...draft, title: v}), {placeholder:'Specialist'})}</div>
            <div style={cell}>{sel(draft.team, v => setDraft({...draft, team: v}), TEAM_OPTIONS, '—')}</div>
            <div style={cell}>{inp(draft.email, v => setDraft({...draft, email: v}), {mono: true, placeholder:'name@adro.com'})}</div>
            <div style={cell}>{sel(draft.planned_role, v => setDraft({...draft, planned_role: v}), ROLE_OPTIONS, '—')}</div>
            <div style={{...cell, justifyContent:'center', color:'var(--fg-muted)'}}>—</div>
            <div style={{...cell, justifyContent:'center'}}>
              <button type="button" onClick={()=>setDraft(null)} title="Cancel"
                style={{ width: 26, height: 26, padding: 0, borderRadius: 6,
                  background:'transparent', border:'1px solid var(--divider)',
                  color:'var(--fg-muted)', cursor:'pointer', fontSize: 13,
                  display:'inline-flex', alignItems:'center', justifyContent:'center' }}>×</button>
            </div>
          </div>
        )}
      </div>
      {canEdit && (
        <div style={{marginTop: 12, display:'flex', gap: 8}}>
          {!draft ? (
            <button className="btn btn-primary" onClick={()=>setDraft({email:'', full_name:'', team:'strategy', planned_role:'viewer'})}>
              {LL.addMember}
            </button>
          ) : (
            <>
              <button className="btn btn-primary" onClick={add} disabled={busy}>{busy ? LL.saving : LL.addMember}</button>
              <button className="btn" onClick={()=>setDraft(null)}>{lang==='ko' ? '취소' : 'Cancel'}</button>
            </>
          )}
        </div>
      )}
    </div>
  );
}

// --- Workstream editor --------------------------------------------------
const WS_PRESETS = [
  '#788c5d','#5a9bc4','#d97757','#e1b84f',
  '#9b7bc7','#e85d75','#3d7a8c','#7a7a7a',
  '#c4886c','#6fb3a0','#4a6fa5','#a87c5f',
];

function WorkstreamEditor({ workstreams, canEdit, lang, setErr, onChanged }) {
  const ko = lang !== 'en';
  const [list, setList] = useState(workstreams);
  const [modal, setModal] = useState(null); // null | {mode:'add'|'edit', row?}
  const [busy, setBusy] = useState(false);

  useEffect(() => { setList(workstreams); }, [workstreams]);

  const LL = {
    add: ko ? '+ 새 워크스트림' : '+ New workstream',
    edit: ko ? '편집' : 'Edit',
    del: ko ? '삭제' : 'Delete',
    up: ko ? '위로' : 'Up',
    down: ko ? '아래로' : 'Down',
    label: ko ? '표시명' : 'Label',
    key: ko ? '키 (영문/한글 식별자)' : 'Key (identifier)',
    color: ko ? '색상' : 'Color',
    saving: ko ? '저장 중…' : 'Saving…',
    empty: ko ? '등록된 워크스트림이 없습니다' : 'No workstreams yet',
    delWarn: (n) => ko
      ? `이 워크스트림을 쓰는 태스크 ${n}개가 있어요.\n정말 삭제하면 해당 태스크의 워크스트림이 비게 됩니다.\n계속하시겠습니까?`
      : `${n} task(s) use this workstream.\nDeleting it will clear their workstream field.\nContinue?`,
    save: ko ? '저장' : 'Save',
    cancel: ko ? '취소' : 'Cancel',
    modalTitleAdd: ko ? '새 워크스트림 추가' : 'Add workstream',
    modalTitleEdit: ko ? '워크스트림 편집' : 'Edit workstream',
    keyHint: ko ? '태스크 DB에 저장되는 식별자. 생성 후 변경 불가.' : 'Stored in task DB. Cannot change after creation.',
  };

  async function reload() {
    try {
      const rows = await window.AOX_DB.fetchWorkstreamConfig();
      setList(rows);
      if (onChanged) onChanged(rows);
    } catch(e) { setErr(e.message); }
  }

  async function handleDelete(ws) {
    const count = await window.AOX_DB.countTasksByWorkstream(ws.key);
    const msg = count > 0 ? LL.delWarn(count) : (ko ? '정말 삭제하시겠습니까?' : 'Delete this workstream?');
    if (!window.confirm(msg)) return;
    setBusy(true);
    try {
      await window.AOX_DB.softDeleteWorkstreamConfig(ws.key);
      await reload();
    } catch(e) { setErr(e.message); }
    finally { setBusy(false); }
  }

  async function handleMove(idx, dir) {
    const next = [...list];
    const target = idx + dir;
    if (target < 0 || target >= next.length) return;
    [next[idx], next[target]] = [next[target], next[idx]];
    const updates = next.map((ws, i) => ({ key: ws.key, sort_order: i + 1 }));
    setList(next.map((ws, i) => ({ ...ws, sort_order: i + 1 })));
    setBusy(true);
    try {
      await window.AOX_DB.reorderWorkstreamConfig(updates);
      if (onChanged) onChanged(next.map((ws, i) => ({ ...ws, sort_order: i + 1 })));
    } catch(e) { setErr(e.message); await reload(); }
    finally { setBusy(false); }
  }

  const dotStyle = (color) => ({
    width: 12, height: 12, borderRadius: '50%',
    background: color, flexShrink: 0, border: '1px solid rgba(0,0,0,0.12)',
  });
  const iconBtn = (onClick, title, danger) => ({
    onClick, title,
    style: {
      padding: '3px 8px', borderRadius: 6, border: '1px solid var(--divider)',
      background: 'transparent', color: danger ? '#FF3B30' : 'var(--fg-muted)',
      cursor: 'pointer', fontSize: 11, fontWeight: 600,
      borderColor: danger ? 'rgba(255,59,48,0.4)' : 'var(--divider)',
    },
  });

  return (
    <div>
      <div style={{ border: '1px solid var(--divider)', borderRadius: 10, overflow: 'hidden' }}>
        <div style={{
          display: 'grid', gridTemplateColumns: '12px 1fr 120px 80px 148px',
          gap: 0, height: 32, alignItems: 'center', padding: '0 12px',
          background: 'var(--surface-2)', fontSize: 11, fontWeight: 600,
          color: 'var(--fg-muted)', letterSpacing: 0.04,
          borderBottom: '1px solid var(--divider)',
        }}>
          <div/>
          <div style={{ paddingLeft: 10 }}>{LL.label}</div>
          <div>{LL.key}</div>
          <div>{LL.color}</div>
          <div/>
        </div>
        {list.length === 0 && (
          <div style={{ padding: 16, textAlign: 'center', color: 'var(--fg-muted)', fontSize: 12 }}>{LL.empty}</div>
        )}
        {list.map((ws, idx) => (
          <div key={ws.key} style={{
            display: 'grid', gridTemplateColumns: '12px 1fr 120px 80px 148px',
            gap: 0, height: 44, alignItems: 'center', padding: '0 12px',
            borderBottom: '1px solid var(--divider)',
          }}>
            <div style={dotStyle(ws.color)}/>
            <div style={{ paddingLeft: 10, fontSize: 13, fontWeight: 600, color: 'var(--fg)' }}>{ws.label}</div>
            <div style={{ fontSize: 11, fontFamily: 'var(--font-mono)', color: 'var(--fg-muted)' }}>{ws.key}</div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
              <div style={{ width: 22, height: 22, borderRadius: 4, background: ws.color, border: '1px solid rgba(0,0,0,0.12)' }}/>
              <span style={{ fontSize: 10, fontFamily: 'var(--font-mono)', color: 'var(--fg-muted)' }}>{ws.color}</span>
            </div>
            {canEdit && (
              <div style={{ display: 'flex', gap: 4, justifyContent: 'flex-end' }}>
                <button {...iconBtn(() => handleMove(idx, -1), LL.up)} disabled={busy || idx === 0}>↑</button>
                <button {...iconBtn(() => handleMove(idx, 1), LL.down)} disabled={busy || idx === list.length - 1}>↓</button>
                <button {...iconBtn(() => setModal({ mode: 'edit', row: ws }), LL.edit)}>{LL.edit}</button>
                <button {...iconBtn(() => handleDelete(ws), LL.del, true)} disabled={busy}>{LL.del}</button>
              </div>
            )}
          </div>
        ))}
      </div>
      {canEdit && (
        <div style={{ marginTop: 12 }}>
          <button className="btn btn-primary" onClick={() => setModal({ mode: 'add' })} disabled={busy}>
            {LL.add}
          </button>
        </div>
      )}
      {modal && (
        <WorkstreamModal
          mode={modal.mode}
          row={modal.row}
          existingKeys={list.map(ws => ws.key)}
          nextOrder={list.length + 1}
          LL={LL}
          onSave={async (payload) => {
            setBusy(true);
            try {
              await window.AOX_DB.upsertWorkstreamConfig(payload);
              setModal(null);
              await reload();
            } catch(e) { setErr(e.message); }
            finally { setBusy(false); }
          }}
          onClose={() => setModal(null)}
          busy={busy}
        />
      )}
    </div>
  );
}

function WorkstreamModal({ mode, row, existingKeys, nextOrder, LL, onSave, onClose, busy }) {
  const isEdit = mode === 'edit';
  const [label, setLabel] = useState(row?.label || '');
  const [key, setKey] = useState(row?.key || '');
  const [color, setColor] = useState(row?.color || WS_PRESETS[0]);
  const [keyTouched, setKeyTouched] = useState(isEdit);

  function autoKey(lbl) {
    return lbl.trim().replace(/\s+/g, '-').toLowerCase();
  }

  function handleLabelChange(v) {
    setLabel(v);
    if (!keyTouched) setKey(autoKey(v));
  }

  function handleSubmit() {
    if (!label.trim()) return;
    if (!key.trim()) return;
    onSave({
      ...(row && !String(row.id).startsWith('default-') ? { id: row.id } : {}),
      key: key.trim(),
      label: label.trim(),
      color,
      sort_order: row?.sort_order ?? nextOrder,
      is_active: true,
    });
  }

  const fieldStyle = {
    width: '100%', boxSizing: 'border-box',
    padding: '7px 10px', borderRadius: 8,
    border: '1px solid var(--border)', background: 'var(--bg-solid)',
    fontSize: 13, color: 'var(--fg)',
  };

  return (
    <div className="modal-backdrop" onClick={onClose}>
      <div className="modal" onClick={e => e.stopPropagation()} style={{ width: 'min(420px, 92vw)' }}>
        <header style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
          <div style={{ fontSize: 14, fontWeight: 700 }}>{isEdit ? LL.modalTitleEdit : LL.modalTitleAdd}</div>
          <button className="btn" onClick={onClose}>{LL.cancel}</button>
        </header>
        <div className="body" style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          <div>
            <label style={{ fontSize: 11, fontWeight: 600, color: 'var(--fg-muted)', display: 'block', marginBottom: 5 }}>{LL.label}</label>
            <input style={fieldStyle} value={label} onChange={e => handleLabelChange(e.target.value)} autoFocus/>
          </div>
          <div>
            <label style={{ fontSize: 11, fontWeight: 600, color: 'var(--fg-muted)', display: 'block', marginBottom: 5 }}>{LL.key}</label>
            <input style={{ ...fieldStyle, fontFamily: 'var(--font-mono)', ...(isEdit ? { color: 'var(--fg-muted)', background: 'var(--surface-2)' } : {}) }}
              value={key}
              onChange={e => { setKeyTouched(true); setKey(e.target.value); }}
              disabled={isEdit}/>
            <div style={{ fontSize: 10.5, color: 'var(--fg-muted)', marginTop: 3 }}>{LL.keyHint}</div>
          </div>
          <div>
            <label style={{ fontSize: 11, fontWeight: 600, color: 'var(--fg-muted)', display: 'block', marginBottom: 8 }}>{LL.color}</label>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginBottom: 10 }}>
              {WS_PRESETS.map(c => (
                <button key={c} type="button"
                  onClick={() => setColor(c)}
                  style={{
                    width: 28, height: 28, borderRadius: 6, background: c, border: 'none',
                    cursor: 'pointer', flexShrink: 0,
                    outline: color === c ? '2px solid var(--accent)' : '2px solid transparent',
                    outlineOffset: 2,
                  }}/>
              ))}
            </div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
              <input type="color" value={color} onChange={e => setColor(e.target.value)}
                style={{ width: 40, height: 32, borderRadius: 6, border: '1px solid var(--border)', cursor: 'pointer', padding: 2 }}/>
              <span style={{ fontSize: 12, fontFamily: 'var(--font-mono)', color: 'var(--fg-muted)' }}>{color}</span>
            </div>
          </div>
        </div>
        <footer style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
          <button className="btn" onClick={onClose} disabled={busy}>{LL.cancel}</button>
          <button className="btn btn-primary" onClick={handleSubmit} disabled={busy || !label.trim() || !key.trim()}>
            {busy ? LL.saving : LL.save}
          </button>
        </footer>
      </div>
    </div>
  );
}

window.AdminSettings = AdminSettings;
