/* eslint-disable */
// =============================================================
// 云版编辑能力 —— 覆盖 window.TaskModal:加状态切换、进度备注、权限
// A:改状态  B:写进度  G:权限(本人 / 协助 / pengtao 可改)
// =============================================================

const STATUS_OPTIONS = [
  { v: 'doing',   label: '进行中', cls: 'st-doing' },
  { v: 'blocked', label: '阻塞',   cls: 'st-blocked' },
  { v: 'paused',  label: '暂缓',   cls: 'st-paused',  bossOnly: true },
  { v: 'done',    label: '完成',   cls: 'st-done',    bossOnly: true },
];

// 老板专属状态:只有彭韬可以把任务设为「暂缓」或「完成」
const BOSS_ONLY_STATUS = new Set(['paused', 'done']);
function isBoss() { return window.CURRENT_USER === 'pengtao'; }

function canEditTask(task) {
  const me = window.CURRENT_USER;
  if (!me) return false;
  if (me === 'pengtao') return true;                       // 负责人:全权
  if (task.owner === me) return true;                      // 自己的
  if ((task.assists || []).includes(me)) return true;      // 协助的
  return false;
}

function fmtTime(iso) {
  const d = new Date(iso);
  const now = new Date();
  const diffH = (now - d) / 3600000;
  if (diffH < 1) return Math.max(1, Math.round(diffH * 60)) + ' 分钟前';
  if (diffH < 24) return Math.round(diffH) + ' 小时前';
  const diffD = Math.round(diffH / 24);
  if (diffD < 30) return diffD + ' 天前';
  return d.toLocaleDateString('zh-CN');
}

function dateInputValue(date) {
  const y = date.getFullYear();
  const m = String(date.getMonth() + 1).padStart(2, '0');
  const d = String(date.getDate()).padStart(2, '0');
  return `${y}-${m}-${d}`;
}

function newTaskDueBounds() {
  const minDate = new Date();
  minDate.setHours(0, 0, 0, 0);
  const maxDate = new Date(minDate);
  maxDate.setDate(maxDate.getDate() + 10);
  return { min: dateInputValue(minDate), max: dateInputValue(maxDate) };
}

function CloudTaskModal({ task, onClose }) {
  const [logs, setLogs] = useState([]);
  const [draft, setDraft] = useState('');
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState(null);
  const owner = U.memberById(task.owner);
  const editable = canEditTask(task);
  const meId = window.CURRENT_USER;
  const me = U.memberById(meId);

  // ----- 任务信息编辑面板(name / priority / dueDate / raw)-----
  const [showEdit, setShowEdit] = useState(false);
  const [editName, setEditName]       = useState(task.name || '');
  const [editPriority, setEditPriority] = useState(task.priority || 'medium');
  const [editDueDate, setEditDueDate]   = useState(task.dueDate || '');
  const [editRaw, setEditRaw]           = useState(task.raw || '');
  // 任务在 realtime 同步后 reload,本组件实例不会重建;只在打开面板时重新拿一次最新值
  function openEdit() {
    setEditName(task.name || '');
    setEditPriority(task.priority || 'medium');
    setEditDueDate(task.dueDate || '');
    setEditRaw(task.raw || '');
    setShowEdit(true);
    setErr(null);
  }
  function cancelEdit() { setShowEdit(false); setErr(null); }
  const editDirty = (
    editName.trim() !== (task.name || '') ||
    editPriority !== (task.priority || 'medium') ||
    (editDueDate || '') !== (task.dueDate || '') ||
    editRaw !== (task.raw || '')
  );

  // 拉日志(进度备注 + 状态变更)
  async function loadLogs() {
    const data = await SB.fetchTaskLogs(task.id);
    setLogs(data || []);
  }
  useEffect(() => { loadLogs(); }, [task.id]);

  async function changeStatus(newStatus) {
    if (newStatus === task.status) return;
    // 防御性兜底:即使有人改了 DOM 绕过 disabled,非彭韬也不能落库
    if (BOSS_ONLY_STATUS.has(newStatus) && !isBoss()) {
      setErr('只有彭韬可以把任务设为「暂缓」或「完成」');
      return;
    }
    setBusy(true); setErr(null);
    try {
      const old = task.status;
      await SB.patchTask(task.id, { status: newStatus }, meId);
      await SB.addLog(task.id, meId, 'status', null, old, newStatus);
      // 本地立即更新(Realtime 也会触发,这里先快显)
      task.status = newStatus;
      task.updatedAt = 0;
      await loadLogs();
      if (window.CW_REFRESH) window.CW_REFRESH().catch(() => {});
    } catch (e) { setErr(e.message || String(e)); }
    setBusy(false);
  }

  // 撤回完成:把状态从 done 改回 doing(默认),并落日志为「撤回完成」
  async function revokeDone() {
    if (!isBoss()) { setErr('只有彭韬可以撤回完成'); return; }
    if (task.status !== 'done') return;
    setBusy(true); setErr(null);
    try {
      await SB.patchTask(task.id, { status: 'doing' }, meId);
      await SB.addLog(task.id, meId, 'revoke', '撤回完成,改回「进行中」', 'done', 'doing');
      task.status = 'doing';
      task.updatedAt = 0;
      await loadLogs();
      if (window.CW_REFRESH) window.CW_REFRESH().catch(() => {});
    } catch (e) { setErr(e.message || String(e)); }
    setBusy(false);
  }

  // 添加协助人:把 memberId 加入 assists 数组,落日志
  async function addAssist(memberId) {
    if (!isBoss()) { setErr('只有彭韬可以管理协助人'); return; }
    if (!memberId || memberId === task.owner) return;
    const current = task.assists || [];
    if (current.includes(memberId)) return;
    const newAssists = [...current, memberId];
    const newName = U.memberById(memberId)?.name || memberId;
    setBusy(true); setErr(null);
    try {
      await SB.patchTask(task.id, { assists: newAssists }, meId);
      await SB.addLog(task.id, meId, 'assists', `添加协助人:${newName}`, null, memberId);
      task.assists = newAssists;
      task.updatedAt = 0;
      await loadLogs();
      if (window.CW_REFRESH) window.CW_REFRESH().catch(() => {});
    } catch (e) { setErr(e.message || String(e)); }
    setBusy(false);
  }

  // 移除协助人:从 assists 数组里去掉 memberId,落日志
  async function removeAssist(memberId) {
    if (!isBoss()) { setErr('只有彭韬可以管理协助人'); return; }
    const current = task.assists || [];
    if (!current.includes(memberId)) return;
    const oldName = U.memberById(memberId)?.name || memberId;
    if (!confirm(`确认把 ${oldName} 从「${task.name}」的协助人里移除吗?`)) return;
    const newAssists = current.filter(id => id !== memberId);
    setBusy(true); setErr(null);
    try {
      await SB.patchTask(task.id, { assists: newAssists }, meId);
      await SB.addLog(task.id, meId, 'assists', `移除协助人:${oldName}`, memberId, null);
      task.assists = newAssists;
      task.updatedAt = 0;
      await loadLogs();
      if (window.CW_REFRESH) window.CW_REFRESH().catch(() => {});
    } catch (e) { setErr(e.message || String(e)); }
    setBusy(false);
  }

  // 转交任务:把 owner_id 改成另一个成员,落日志为「转交」
  async function transferTo(newOwnerId) {
    if (!isBoss()) { setErr('只有彭韬可以转交任务'); return; }
    if (!newOwnerId || newOwnerId === task.owner) return;
    const oldOwner = task.owner;
    const oldName = U.memberById(oldOwner)?.name || oldOwner;
    const newName = U.memberById(newOwnerId)?.name || newOwnerId;
    if (!confirm(`确认把「${task.name}」从 ${oldName} 转交给 ${newName} 吗?`)) return;
    setBusy(true); setErr(null);
    try {
      await SB.patchTask(task.id, { owner: newOwnerId }, meId);
      await SB.addLog(task.id, meId, 'transfer', `转交:${oldName} → ${newName}`, oldOwner, newOwnerId);
      task.owner = newOwnerId;
      task.updatedAt = 0;
      await loadLogs();
      if (window.CW_REFRESH) window.CW_REFRESH().catch(() => {});
    } catch (e) { setErr(e.message || String(e)); }
    setBusy(false);
  }

  // 软删除任务(仅彭韬)
  async function deleteTask() {
    if (!isBoss()) { setErr('只有彭韬可以删除任务'); return; }
    const msg = `确认删除任务「${task.name}」吗?\n\n删除后该任务从所有人看板上消失,但历史日志保留(可恢复)。`;
    if (!confirm(msg)) return;
    setBusy(true); setErr(null);
    try {
      await SB.patchTask(task.id, { deleted: true }, meId);
      await SB.addLog(task.id, meId, 'delete', `删除任务:${task.name}`);
      // 关闭弹窗 —— 这条任务已经在数据库被标记 deleted,realtime 同步后会从看板消失
      onClose();
    } catch (e) {
      setErr(e.message || String(e));
      setBusy(false);
    }
  }

  // 保存任务信息编辑(name / priority / dueDate / raw)
  async function saveEdit() {
    if (!editable) { setErr('你没有编辑这条任务的权限'); return; }
    const name = editName.trim();
    if (!name) { setErr('任务名不能为空'); return; }
    if (!editDirty) { setShowEdit(false); return; }
    setBusy(true); setErr(null);
    try {
      const patch = {};
      const changes = []; // 累计改动文字,用于日志
      if (name !== (task.name || '')) {
        patch.name = name;
        changes.push(`名称:${task.name || '(空)'} → ${name}`);
      }
      if (editPriority !== (task.priority || 'medium')) {
        patch.priority = editPriority;
        changes.push(`优先级:${prioLabel(task.priority)} → ${prioLabel(editPriority)}`);
      }
      const newDue = editDueDate || null;
      const oldDue = task.dueDate || null;
      if (newDue !== oldDue) {
        patch.dueDate = newDue;
        changes.push(`截止:${oldDue || '无'} → ${newDue || '无'}`);
      }
      if (editRaw !== (task.raw || '')) {
        patch.raw = editRaw;
        changes.push('说明已更新');
      }
      await SB.patchTask(task.id, patch, meId);
      await SB.addLog(task.id, meId, 'edit', changes.join(' · '));
      // 同步本地视图
      if ('name'     in patch) task.name     = patch.name;
      if ('priority' in patch) task.priority = patch.priority;
      if ('dueDate'  in patch) task.dueDate  = patch.dueDate;
      if ('raw'      in patch) task.raw      = patch.raw;
      task.updatedAt = 0;
      setShowEdit(false);
      await loadLogs();
      if (window.CW_REFRESH) window.CW_REFRESH().catch(() => {});
    } catch (e) { setErr(e.message || String(e)); }
    setBusy(false);
  }

  async function postProgress() {
    if (!draft.trim()) return;
    setBusy(true); setErr(null);
    try {
      const content = draft.trim();
      const mentions = SB.parseMentions ? SB.parseMentions(content) : [];
      await SB.addLog(task.id, meId, 'progress', content, null, null, mentions);
      // 同时拍一下 task 的 updated_at,让"僵尸"标签消失
      await SB.patchTask(task.id, {}, meId);
      task.updatedAt = 0;
      setDraft('');
      await loadLogs();
      if (window.CW_REFRESH) window.CW_REFRESH().catch(() => {});
    } catch (e) { setErr(e.message || String(e)); }
    setBusy(false);
  }

  return (
    <div className="modal-bg" onClick={onClose}>
      <div className="modal" onClick={e=>e.stopPropagation()}>
        <div className="modal-head">
          <UrgencyDot task={task}/>
          <h3>{task.name}</h3>
          <button className="x" onClick={onClose}>×</button>
        </div>

        <div className="modal-meta">
          <div>
            <span className="lbl">负责人</span>
            <Avatar id={task.owner} size={20}/>
            {isBoss() ? (
              <select className="ce-owner-sel"
                value={task.owner}
                disabled={busy}
                onChange={e=>transferTo(e.target.value)}>
                {window.MEMBERS.map(m =>
                  <option key={m.id} value={m.id}>
                    {m.name}{m.id===task.owner?' (当前)':''}
                  </option>
                )}
              </select>
            ) : (
              <span>{owner?.name}</span>
            )}
            {isBoss() && <span className="ce-hint-inline">改下拉即转交</span>}
          </div>
          <div><span className="lbl">截止</span><span>{U.dueLabel(task)}{task.dueDate?` (${task.dueDate})`:''}</span></div>
          <div><span className="lbl">优先级</span><span>{prioLabel(task.priority)}</span></div>
          <div><span className="lbl">最近更新</span><span>{task.updatedAt} 天前</span></div>
          {isBoss() ? (
            <div>
              <span className="lbl">协助</span>
              {(task.assists || []).map(a => {
                const m = U.memberById(a);
                return (
                  <span key={a} className="ce-assist-chip" title={`点 × 移除 ${m?.name || a}`}>
                    <Avatar id={a} size={18}/>
                    <span className="ce-assist-name">{m?.short || m?.name || a}</span>
                    <button className="ce-assist-x" disabled={busy} onClick={()=>removeAssist(a)}>×</button>
                  </span>
                );
              })}
              <select className="ce-assist-add"
                value=""
                disabled={busy}
                onChange={e => { addAssist(e.target.value); e.target.value=''; }}>
                <option value="">+ 加协助人</option>
                {window.MEMBERS
                  .filter(m => m.id !== task.owner && !(task.assists || []).includes(m.id))
                  .map(m => <option key={m.id} value={m.id}>{m.name}</option>)}
              </select>
            </div>
          ) : (task.assists && task.assists.length>0 &&
            <div><span className="lbl">协助</span>{task.assists.map(a=>(<Avatar key={a} id={a} size={18}/>))}</div>
          )}
          {task.raw && <div><span className="lbl">说明</span><span className="raw">{task.raw}</span></div>}
        </div>

        {/* ===== A0. 编辑任务信息(可折叠)===== */}
        {editable && (
          <div className="ce-section ce-edit">
            {!showEdit ? (
              <div className="ce-edit-trigger">
                <button className="ce-edit-btn" onClick={openEdit} disabled={busy}>
                  ✏️ 编辑任务信息(名称 · 优先级 · 截止日 · 说明)
                </button>
              </div>
            ) : (
              <div className="ce-edit-form">
                <div className="ce-section-h"><span>编辑任务信息</span></div>
                <label className="ce-edit-field">
                  <span className="ce-edit-lbl">任务名 *</span>
                  <input value={editName} onChange={e=>setEditName(e.target.value)} disabled={busy}
                         placeholder="必填" />
                </label>
                <div className="ce-edit-row">
                  <label className="ce-edit-field ce-edit-half">
                    <span className="ce-edit-lbl">优先级</span>
                    <select value={editPriority} onChange={e=>setEditPriority(e.target.value)} disabled={busy}>
                      <option value="high">高</option>
                      <option value="medium">中</option>
                      <option value="low">低</option>
                    </select>
                  </label>
                  <label className="ce-edit-field ce-edit-half">
                    <span className="ce-edit-lbl">截止日(留空表示常态任务)</span>
                    <input type="date" value={editDueDate} onChange={e=>setEditDueDate(e.target.value)} disabled={busy}/>
                  </label>
                </div>
                <label className="ce-edit-field">
                  <span className="ce-edit-lbl">说明 / 备注</span>
                  <textarea rows="2" value={editRaw} onChange={e=>setEditRaw(e.target.value)} disabled={busy}
                            placeholder="可选,例:本周内 / 配合谁 / 现场要带什么"/>
                </label>
                <div className="ce-edit-actions">
                  <button className="btn-secondary" onClick={cancelEdit} disabled={busy}>取消</button>
                  <button className="btn-primary" onClick={saveEdit} disabled={busy || !editDirty || !editName.trim()}>
                    {busy ? '保存中…' : (editDirty ? '保存改动' : '无改动')}
                  </button>
                </div>
              </div>
            )}
          </div>
        )}

        {/* ===== A. 状态切换 ===== */}
        <div className="ce-section">
          <div className="ce-section-h">
            <span>状态</span>
            {!editable && <span className="ce-lock">🔒 你不能改这条(只有负责人/协助人/彭韬能改)</span>}
            {editable && !isBoss() && <span className="ce-lock">🔒「暂缓」「完成」仅彭韬可设置</span>}
          </div>
          <div className="ce-status-row">
            {STATUS_OPTIONS.map(o => {
              const bossLocked = o.bossOnly && !isBoss();
              const isCurrent = task.status === o.v;
              // 当前状态如果已经是 paused/done(由彭韬设置过),按钮仍然亮起表明状态,
              // 但非彭韬点击它无效;切到其他状态需要彭韬本人。
              return (
                <button key={o.v}
                  className={`ce-st ${o.cls} ${isCurrent?'on':''} ${bossLocked?'boss-locked':''}`}
                  disabled={!editable || busy || (bossLocked && !isCurrent)}
                  title={bossLocked ? '只有彭韬可设置此状态' : ''}
                  onClick={()=>changeStatus(o.v)}>
                  {o.label}{bossLocked && !isCurrent ? ' 🔒' : ''}
                </button>
              );
            })}
          </div>
          {/* 已完成任务:彭韬可一键撤回 */}
          {task.status === 'done' && isBoss() && (
            <div className="ce-revoke-bar">
              <span>✓ 此任务已标记完成。</span>
              <button className="ce-revoke-btn" disabled={busy} onClick={revokeDone}>
                ↻ 撤回完成,改回「进行中」
              </button>
            </div>
          )}
        </div>

        {/* ===== B. 进度备注 ===== */}
        <div className="ce-section">
          <div className="ce-section-h">
            <span>进度日志</span>
            <span className="ce-sub">{logs.length} 条</span>
          </div>
          {editable && (
            <div className="ce-input">
              <textarea
                rows="2"
                placeholder={`以 ${me?.name || '我'} 身份记一行今天的进度…(回车提交,Shift+回车换行)`}
                value={draft}
                onChange={e=>setDraft(e.target.value)}
                onKeyDown={e=>{
                  if (e.key==='Enter' && !e.shiftKey) { e.preventDefault(); postProgress(); }
                }}
                disabled={busy}/>
              <button className="btn-primary" onClick={postProgress} disabled={busy || !draft.trim()}>
                {busy ? '保存中…' : '记录'}
              </button>
            </div>
          )}
          {err && <div className="ce-err">{err}</div>}
          <div className="ce-log-list">
            {logs.length === 0 && <div className="cmt-empty">暂无日志。{editable && '在上方写一行开始。'}</div>}
            {logs.map(l => {
              const m = U.memberById(l.member_id);
              return (
                <div className={`ce-log ce-log-${l.kind}`} key={l.id}>
                  <Avatar id={l.member_id} size={22}/>
                  <div className="ce-log-body">
                    <div className="ce-log-head">
                      <b>{m?.name || l.member_id}</b>
                      <span className="ce-log-at">{fmtTime(l.created_at)}</span>
                      {l.kind === 'status'   && <span className="ce-kind-tag">改状态</span>}
                      {l.kind === 'create'   && <span className="ce-kind-tag">创建</span>}
                      {l.kind === 'transfer' && <span className="ce-kind-tag ce-kind-transfer">转交</span>}
                      {l.kind === 'revoke'   && <span className="ce-kind-tag ce-kind-revoke">撤回</span>}
                      {l.kind === 'assists'  && <span className="ce-kind-tag ce-kind-assists">协助</span>}
                      {l.kind === 'edit'     && <span className="ce-kind-tag ce-kind-edit">编辑</span>}
                      {l.kind === 'delete'   && <span className="ce-kind-tag ce-kind-delete">删除</span>}
                    </div>
                    <div className="ce-log-text">
                      {l.kind === 'status'
                        ? <>{statusLabel(l.old_value)} → <b>{statusLabel(l.new_value)}</b></>
                        : l.kind === 'transfer'
                        ? <>{U.memberById(l.old_value)?.name || l.old_value} → <b>{U.memberById(l.new_value)?.name || l.new_value}</b></>
                        : l.kind === 'revoke'
                        ? <>{statusLabel(l.old_value)} → <b>{statusLabel(l.new_value)}</b>(撤回完成)</>
                        : l.kind === 'progress'
                        ? highlightMentions(l.content, meId)
                        : l.content}
                    </div>
                  </div>
                </div>
              );
            })}
          </div>
        </div>

        {/* ===== Z. 危险区:仅彭韬可见,删除任务 ===== */}
        {isBoss() && (
          <div className="ce-danger-zone">
            <button className="ce-delete-btn" disabled={busy} onClick={deleteTask}>
              🗑 删除此任务
            </button>
            <span className="ce-danger-hint">软删除 · 历史日志保留 · 可由后台恢复</span>
          </div>
        )}
      </div>
    </div>
  );
}

function statusLabel(v) {
  if (v === 'todo') return '进行中';
  const o = STATUS_OPTIONS.find(x=>x.v===v);
  return o ? o.label : (v || '—');
}
function prioLabel(v) {
  return ({ high: '高', medium: '中', low: '低', paused: '暂缓' })[v] || (v || '中');
}

// 把进度文字里的 @某人 渲染成高亮 chip
// 当前用户被 @ 时,chip 会特别亮一点(.me 修饰)
function highlightMentions(text, meId) {
  if (!text) return text;
  const list = window.MEMBERS || [];
  if (!list.length) return text;
  // 收集所有 (id, displayText) 候选,长的先匹配
  const candidates = [];
  for (const m of list) {
    if (m.name)  candidates.push({ id: m.id, t: m.name });
    if (m.short && m.short !== m.name) candidates.push({ id: m.id, t: m.short });
  }
  candidates.sort((a, b) => b.t.length - a.t.length);
  // 用占位符法切片:先把每个 @候选 替换成 ▌id▌候选▌,再统一渲染
  let working = text;
  const SEP = '\x00';
  for (const c of candidates) {
    working = working.split('@' + c.t).join(SEP + c.id + SEP + c.t + SEP);
  }
  const parts = working.split(SEP);
  // parts 结构:[plain, id, name, plain, id, name, plain, ...]
  // 但实际并不一定每段都成对;只有标记成 id+name 三元组的才是 mention
  // 上面的 split 会让 plain 段保留原文,id+name 配对出现于 mention 处
  const out = [];
  let i = 0;
  while (i < parts.length) {
    const piece = parts[i];
    // 判断是否是合法 id(在 members 列表里能找到)
    if (i + 2 < parts.length && list.some(m => m.id === piece)) {
      const id = piece;
      const display = parts[i + 1];
      // skip empty separator at parts[i+2]
      const isMe = id === meId;
      out.push(
        <span key={'m' + out.length} className={`ce-mention ${isMe ? 'ce-mention-me' : ''}`}>@{display}</span>
      );
      i += 3;
    } else {
      if (piece) out.push(piece);
      i += 1;
    }
  }
  return out;
}

// 覆盖原 TaskModal
window.TaskModal = CloudTaskModal;

// =============================================================
// B2. 新增团队成员(仅彭韬)
// =============================================================
const MEMBER_COLORS = ['#1f6feb', '#2d8a4e', '#0a7e8c', '#7e3aa8', '#b8860b', '#c4631a', '#8a8e95'];

function makeTempMemberId() {
  return 'm_' + Math.random().toString(36).slice(2, 8);
}
function sanitizeMemberId(raw) {
  const cleaned = String(raw || '')
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9_]+/g, '_')
    .replace(/^_+|_+$/g, '')
    .replace(/_{2,}/g, '_')
    .slice(0, 32);
  if (!cleaned) return '';
  return /^[a-z]/.test(cleaned) ? cleaned : 'm_' + cleaned;
}
function defaultMemberShort(name) {
  const text = String(name || '').trim();
  if (!text) return '';
  return /[^\x00-\x7F]/.test(text) ? text.slice(-2) : text.slice(0, 8);
}
function suggestMemberId(name, email, fallback) {
  const emailName = String(email || '').split('@')[0];
  const fromEmail = sanitizeMemberId(emailName);
  if (fromEmail) return fromEmail;
  const plainName = String(name || '').trim();
  if (plainName && /^[\x00-\x7F]+$/.test(plainName)) {
    const fromName = sanitizeMemberId(plainName);
    if (fromName) return fromName;
  }
  return fallback || makeTempMemberId();
}

function CloudNewMember() {
  if (!isBoss()) return null;

  const groups = window.GROUPS || [{ id: 'tba', name: '待补充' }];
  const initialId = makeTempMemberId();
  const [open, setOpen] = useState(false);
  const [name, setName] = useState('');
  const [short, setShort] = useState('');
  const [memberId, setMemberId] = useState(initialId);
  const [idTouched, setIdTouched] = useState(false);
  const [role, setRole] = useState('');
  const [email, setEmail] = useState('');
  const [note, setNote] = useState('');
  const [groupId, setGroupId] = useState(groups[groups.length - 1]?.id || 'tba');
  const [color, setColor] = useState(MEMBER_COLORS[0]);
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState(null);

  useEffect(() => {
    if (idTouched) return;
    setMemberId(current => suggestMemberId(name, email, current));
  }, [name, email, idTouched]);

  function reset() {
    setName('');
    setShort('');
    setMemberId(makeTempMemberId());
    setIdTouched(false);
    setRole('');
    setEmail('');
    setNote('');
    setGroupId(groups[groups.length - 1]?.id || 'tba');
    setColor(MEMBER_COLORS[0]);
    setErr(null);
  }
  function close() {
    setOpen(false);
    reset();
  }
  function setManualId(v) {
    setIdTouched(true);
    setMemberId(sanitizeMemberId(v));
  }
  async function submit() {
    const cleanName = name.trim();
    const cleanId = sanitizeMemberId(memberId);
    const cleanShort = (short.trim() || defaultMemberShort(cleanName)).slice(0, 8);
    if (!cleanName) { setErr('姓名必填'); return; }
    if (!cleanId || !/^[a-z][a-z0-9_]{2,31}$/.test(cleanId)) {
      setErr('成员 ID 需要小写字母开头,只用小写字母/数字/下划线,至少 3 位');
      return;
    }
    if ((window.MEMBERS || []).some(m => m.id === cleanId)) {
      setErr('这个成员 ID 已经存在,请换一个');
      return;
    }
    setBusy(true); setErr(null);
    try {
      const saved = await SB.createMember({
        id: cleanId,
        name: cleanName,
        short: cleanShort,
        role: role.trim() || '待补充',
        email: email.trim() || null,
        note: note.trim() || null,
        color,
        group_id: groupId || 'tba',
      });
      window.MEMBERS = [...(window.MEMBERS || []).filter(m => m.id !== saved.id), saved]
        .sort((a, b) => a.name.localeCompare(b.name, 'zh-Hans-CN-u-co-pinyin'));
      close();
      if (window.CW_RELOAD) window.CW_RELOAD();
    } catch (e) {
      let msg = e.message || String(e);
      setErr(msg);
    }
    setBusy(false);
  }

  return (
    <>
      <button className="cw-fab cw-fab-member" onClick={()=>setOpen(true)} title="新增团队成员">
        <span>＋</span><span className="cw-fab-lbl">成员</span>
      </button>
      {open && (
        <div className="modal-bg" onClick={close}>
          <div className="modal cw-new-modal cw-member-modal" onClick={e=>e.stopPropagation()}>
            <div className="modal-head">
              <h3>新增团队成员</h3>
              <button className="x" onClick={close}>×</button>
            </div>
            <div className="cw-new-form">
              <div className="cw-new-row">
                <label className="cw-half">
                  <span className="cw-lbl">姓名 *</span>
                  <input value={name} onChange={e=>setName(e.target.value)}
                    placeholder="例:李欣" autoFocus />
                </label>
                <label className="cw-half">
                  <span className="cw-lbl">简称</span>
                  <input value={short} onChange={e=>setShort(e.target.value)}
                    placeholder={defaultMemberShort(name) || '头像显示'} />
                </label>
              </div>
              <label>
                <span className="cw-lbl">成员 ID *</span>
                <input value={memberId} onChange={e=>setManualId(e.target.value)}
                  placeholder="例:lixin" />
                <span className="cw-hint">用于任务归属和数据库主键,保存后不要随意改</span>
              </label>
              <div className="cw-new-row">
                <label className="cw-half">
                  <span className="cw-lbl">分组</span>
                  <select value={groupId} onChange={e=>setGroupId(e.target.value)}>
                    {groups.map(g => <option key={g.id} value={g.id}>{g.name}</option>)}
                  </select>
                </label>
                <label className="cw-half">
                  <span className="cw-lbl">头像色</span>
                  <div className="cw-color-row">
                    {MEMBER_COLORS.map(c => (
                      <button key={c} type="button"
                        className={'cw-color-swatch ' + (color === c ? 'on' : '')}
                        style={{ background: c }}
                        onClick={()=>setColor(c)}
                        aria-label={c}/>
                    ))}
                    <input type="color" value={color} onChange={e=>setColor(e.target.value)} />
                  </div>
                </label>
              </div>
              <label>
                <span className="cw-lbl">角色 / 方向</span>
                <input value={role} onChange={e=>setRole(e.target.value)}
                  placeholder="例:喀斯特水文 / 在读研究生" />
              </label>
              <div className="cw-new-row">
                <label className="cw-half">
                  <span className="cw-lbl">邮箱</span>
                  <input value={email} onChange={e=>setEmail(e.target.value)}
                    placeholder="可选" />
                </label>
                <label className="cw-half">
                  <span className="cw-lbl">备注</span>
                  <input value={note} onChange={e=>setNote(e.target.value)}
                    placeholder="例:博士 / 硕士 / 站长" />
                </label>
              </div>
              {err && <div className="ce-err">{err}</div>}
              <div className="cw-new-actions">
                <button className="btn-secondary" onClick={close}>取消</button>
                <button className="btn-primary" onClick={submit} disabled={busy || !name.trim()}>
                  {busy ? '保存中…' : '保存成员'}
                </button>
              </div>
            </div>
          </div>
        </div>
      )}
    </>
  );
}

// =============================================================
// C. 新建任务
// =============================================================
function CloudNewTask() {
  const [open, setOpen] = useState(false);
  const [name, setName] = useState('');
  const [ownerId, setOwnerId] = useState(window.CURRENT_USER);
  const [priority, setPriority] = useState('medium');
  const [dueDate, setDueDate] = useState('');
  const [raw, setRaw] = useState('');
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState(null);

  const meId = window.CURRENT_USER;
  const isAdmin = meId === 'pengtao';
  const dueBounds = newTaskDueBounds();

  function reset() {
    setName(''); setOwnerId(meId); setPriority('medium');
    setDueDate(''); setRaw(''); setErr(null);
  }
  function close() { setOpen(false); reset(); }

  async function submit() {
    if (!name.trim()) { setErr('任务名必填'); return; }
    if (!dueDate) { setErr('截止日期必填'); return; }
    if (dueDate < dueBounds.min) { setErr('截止日期不能早于今天'); return; }
    if (dueDate > dueBounds.max) { setErr('截止日期不能超过 10 天'); return; }
    setBusy(true); setErr(null);
    try {
      const id = 'tu_' + Math.random().toString(36).slice(2, 10);
      await SB.createTask({
        id, name: name.trim(),
        owner_id: ownerId,
        priority, status: 'doing',
        due_date: dueDate,
        raw: raw.trim() || null,
        created_by: meId, updated_by: meId,
      }, meId);
      await SB.addLog(id, meId, 'create', '(新建任务)');
      if (window.CW_REFRESH) await window.CW_REFRESH();
      close();
    } catch (e) { setErr(e.message || String(e)); }
    setBusy(false);
  }

  return (
    <>
      <button className="cw-fab" onClick={()=>setOpen(true)} title="新建任务">
        <span>＋</span><span className="cw-fab-lbl">新任务</span>
      </button>
      {open && (
        <div className="modal-bg" onClick={close}>
          <div className="modal cw-new-modal" onClick={e=>e.stopPropagation()}>
            <div className="modal-head">
              <h3>新建任务</h3>
              <button className="x" onClick={close}>×</button>
            </div>
            <div className="cw-new-form">
              <label>
                <span className="cw-lbl">任务名 *</span>
                <input value={name} onChange={e=>setName(e.target.value)}
                  placeholder="一句话描述,例:阴离子测试" autoFocus
                  onKeyDown={e=>e.key==='Enter' && submit()}/>
              </label>
              <label>
                <span className="cw-lbl">负责人 *</span>
                <select value={ownerId} onChange={e=>setOwnerId(e.target.value)} disabled={!isAdmin}>
                  {window.MEMBERS.map(m =>
                    <option key={m.id} value={m.id}>{m.name}{m.id===meId?'(我)':''}</option>
                  )}
                </select>
                {!isAdmin && <span className="cw-hint">(非负责人只能给自己派任务)</span>}
              </label>
              <div className="cw-new-row">
                <label className="cw-half">
                  <span className="cw-lbl">优先级</span>
                  <select value={priority} onChange={e=>setPriority(e.target.value)}>
                    <option value="high">高</option>
                    <option value="medium">中</option>
                    <option value="low">低</option>
                  </select>
                </label>
                <label className="cw-half">
                  <span className="cw-lbl">截止日 *</span>
                  <input type="date" value={dueDate}
                    min={dueBounds.min}
                    max={dueBounds.max}
                    required
                    onChange={e=>setDueDate(e.target.value)}/>
                  <span className="cw-hint">必须填写,最晚 {dueBounds.max}</span>
                </label>
              </div>
              <label>
                <span className="cw-lbl">备注</span>
                <textarea rows="2" value={raw} onChange={e=>setRaw(e.target.value)}
                  placeholder="可选,例:本周内 / 配合谁 等"/>
              </label>
              {err && <div className="ce-err">{err}</div>}
              <div className="cw-new-actions">
                <button className="btn-secondary" onClick={close}>取消</button>
                <button className="btn-primary" onClick={submit} disabled={busy || !name.trim() || !dueDate}>
                  {busy ? '创建中…' : '创建'}
                </button>
              </div>
            </div>
          </div>
        </div>
      )}
    </>
  );
}

Object.assign(window, { CloudNewTask, CloudNewMember });
