/* global React */
// Submissions: list + compare detail drawer.

function SubmissionsPage({ toast, focusId, onClearFocus, role = 'super-admin' }) {
  const [filter, setFilter] = React.useState('pending');
  const [search, setSearch] = React.useState('');
  const [selected, setSelected] = React.useState(new Set());
  const [openId, setOpenId] = React.useState(null);
  const [focused, setFocused] = React.useState(0);
  const [rejecting, setRejecting] = React.useState(null);
  const [rejectReason, setRejectReason] = React.useState('');
  const [batchReject, setBatchReject] = React.useState(false);
  const [rows, setRows] = React.useState(window.MOCK.SUBMISSIONS);
  const [loading, setLoading] = React.useState(false);
  const [counts, setCounts] = React.useState({ pending: 0, approved: 0, rejected: 0, merged: 0 });
  const isReviewer = role === 'reviewer';

  const reload = React.useCallback(async () => {
    setLoading(true);
    try {
      const [r, s] = await Promise.all([
        window.API.submissions(filter === 'all' ? 'pending' : filter, 200),
        window.API.stats().catch(() => ({ counts: {} })),
      ]);
      if (r?.items) setRows(r.items.map(window.normalizeSubmission));
      if (s?.counts) setCounts({ pending: 0, approved: 0, rejected: 0, merged: 0, ...s.counts });
    } catch (e) {
      // Demo mode fallback — keep MOCK
    } finally {
      setLoading(false);
    }
  }, [filter]);

  React.useEffect(() => { reload(); }, [reload]);
  const maskIp = (ip) => {
    if (!isReviewer) return ip;
    // submitter_short is like "198.51.x.42" — reduce further to "198.x.x.x"
    const parts = ip.split('.');
    return parts.length >= 2 ? `${parts[0]}.x.x.x` : ip;
  };

  React.useEffect(() => {
    if (focusId) {
      setOpenId(focusId);
      onClearFocus?.();
    }
  }, [focusId]);

  const list = React.useMemo(() => {
    return rows.filter((s) => {
      if (filter !== 'all' && s.status !== filter) return false;
      if (search && !(`${s.id} ${s.area_code} ${s.npc} ${s.note || ''}`.toLowerCase().includes(search.toLowerCase()))) return false;
      return true;
    });
  }, [filter, search, rows]);

  const doApprove = async (id) => {
    try {
      await window.API.approve(id);
      toast.push(`已通过 #${id}`);
      reload();
    } catch (e) { toast.push(`操作失败:${e.message}`, 'err'); }
  };

  const doReject = async (id, reason) => {
    try {
      await window.API.reject(id, reason);
      toast.push(`已驳回 #${id}`);
      reload();
    } catch (e) { toast.push(`操作失败:${e.message}`, 'err'); }
  };

  // Keyboard navigation
  React.useEffect(() => {
    if (openId || rejecting) return;
    const onKey = (e) => {
      if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
      if (e.key === 'j') { e.preventDefault(); setFocused((i) => Math.min(list.length - 1, i + 1)); }
      else if (e.key === 'k') { e.preventDefault(); setFocused((i) => Math.max(0, i - 1)); }
      else if (e.key === 'Enter') {
        e.preventDefault();
        const item = list[focused];
        if (item) setOpenId(item.id);
      }
      else if (e.key === ' ') {
        e.preventDefault();
        const item = list[focused];
        if (item) {
          const ns = new Set(selected);
          ns.has(item.id) ? ns.delete(item.id) : ns.add(item.id);
          setSelected(ns);
        }
      }
      else if (e.key === 'a') {
        const item = list[focused];
        if (item && item.status === 'pending') doApprove(item.id);
      }
      else if (e.key === 'r') {
        const item = list[focused];
        if (item && item.status === 'pending') setRejecting(item);
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [list, focused, openId, rejecting, selected]);

  const open = openId ? rows.find((s) => s.id === openId) : null;
  const toggleAll = () => {
    if (selected.size === list.length) setSelected(new Set());
    else setSelected(new Set(list.map((x) => x.id)));
  };
  const toggle = (id) => {
    const ns = new Set(selected);
    ns.has(id) ? ns.delete(id) : ns.add(id);
    setSelected(ns);
  };

  return (
    <div>
      <div className="page-header">
        <div>
          <div className="breadcrumb"><span>日常</span><span className="sep">/</span><span>审核台</span></div>
          <h1 className="page-title">审核台 <span style={{ color: 'var(--text-3)', fontWeight: 400 }}>· {list.length}</span></h1>
          <div className="page-subtitle">
            按 <window.KBD>J</window.KBD>/<window.KBD>K</window.KBD> 上下移动 ·
            <window.KBD>↵</window.KBD> 打开详情 ·
            <window.KBD>A</window.KBD> 通过 ·
            <window.KBD>R</window.KBD> 驳回 ·
            <window.KBD>Space</window.KBD> 多选
          </div>
        </div>
      </div>

      {/* Toolbar */}
      <div className="hstack" style={{ marginBottom: 16, flexWrap:'wrap' }}>
        <FilterTabs value={filter} onChange={setFilter} options={[
          { v: 'pending', label: '待审', count: counts.pending },
          { v: 'approved', label: '已通过', count: counts.approved },
          { v: 'rejected', label: '已驳回', count: counts.rejected },
          { v: 'merged', label: '已合并', count: counts.merged },
          { v: 'all', label: '全部', count: (counts.pending+counts.approved+counts.rejected+counts.merged) },
        ]}/>
        <div style={{ flex: 1 }}/>
        <div className="hstack" style={{ position:'relative' }}>
          <window.I.search size={14} style={{ position:'absolute', left: 10, color: 'var(--text-3)' }}/>
          <input className="input" placeholder="搜索 ID / NPC / 备注"
                 value={search} onChange={(e) => setSearch(e.target.value)}
                 style={{ width: 240, paddingLeft: 32 }}/>
        </div>
        <window.Btn icon={<window.I.filter size={14}/>}>区域 · 全部</window.Btn>
        <window.Btn icon={<window.I.download size={14}/>}>导出</window.Btn>
      </div>

      {/* Batch bar */}
      {selected.size > 0 && (
        <div className="card" style={{ padding: '10px 16px', marginBottom: 12,
          display:'flex', alignItems:'center', gap: 12,
          borderColor: 'var(--indigo)', background: 'var(--primary-50)' }}>
          <span style={{ fontWeight: 500 }}>已选 {selected.size} 条</span>
          <span className="muted">·</span>
          <window.Btn size="sm" kind="success" icon={<window.I.check size={12}/>}
                      onClick={async () => {
                        const ids = Array.from(selected);
                        await Promise.all(ids.map((id) => window.API.approve(id).catch(()=>null)));
                        toast.push(`已批量通过 ${ids.length} 条`); setSelected(new Set()); reload();
                      }}>
            批量通过
          </window.Btn>
          <window.Btn size="sm" kind="danger" icon={<window.I.x size={12}/>}
                      onClick={() => setBatchReject(true)}>
            批量驳回
          </window.Btn>
          <window.Btn size="sm">合并到现有 NPC</window.Btn>
          <button className="btn ghost sm" onClick={() => setSelected(new Set())} style={{ marginLeft:'auto' }}>清除选择</button>
        </div>
      )}

      {/* Table */}
      <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
        <table className="table">
          <thead>
            <tr>
              <th style={{ width: 40 }}>
                <input type="checkbox" checked={selected.size === list.length && list.length > 0}
                       onChange={toggleAll}/>
              </th>
              <th style={{ width: 56 }}>ID</th>
              <th style={{ width: 88 }}>状态</th>
              <th style={{ width: 140 }}>区域 · NPC</th>
              <th>阵容预览</th>
              <th style={{ width: 180 }}>元信息</th>
              <th>备注</th>
              <th style={{ width: 100 }}>提交者</th>
              <th style={{ width: 96 }}>时间</th>
              <th style={{ width: 180 }}></th>
            </tr>
          </thead>
          <tbody>
            {list.map((s, i) => (
              <tr key={s.id}
                  className={`${selected.has(s.id) ? 'selected' : ''} ${i === focused ? 'focused' : ''}`}
                  onClick={() => setFocused(i)}>
                <td onClick={(e) => e.stopPropagation()}>
                  <input type="checkbox" checked={selected.has(s.id)}
                         onChange={() => toggle(s.id)}/>
                </td>
                <td className="mono" style={{ color: 'var(--text-3)' }}>#{s.id}</td>
                <td><window.Pill status={s.status}/></td>
                <td>
                  <div style={{ fontWeight: 500 }}>{s.npc}</div>
                  <div className="muted" style={{ fontSize: 11, fontFamily:'var(--font-mono)' }}>{s.area_code}</div>
                </td>
                <td>
                  <div style={{ display:'flex', gap: 4 }}>
                    {s.team.slice(0, 6).map((p, j) => (
                      <window.Sprite key={j} name={p.name} color={p.color} size="sm" label={p.name}/>
                    ))}
                  </div>
                </td>
                <td>
                  <div style={{ display:'flex', gap: 4, flexWrap:'wrap' }}>
                    <Chip>速 {s.speed}</Chip>
                    <Chip>{s.weather}</Chip>
                    {s.ding && <Chip>定</Chip>}
                    {s.lock && <Chip>锁</Chip>}
                    {s.chu  && <Chip>初</Chip>}
                  </div>
                </td>
                <td>
                  <div style={{ maxWidth: 280, color: 'var(--text-2)', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>
                    {s.note || <span className="dim">—</span>}
                  </div>
                </td>
                <td>
                  <div className="mono" style={{ fontSize: 12, color: 'var(--text-2)' }}>{maskIp(s.submitter_short)}</div>
                </td>
                <td className="muted" style={{ fontSize: 12 }} title={s.created_at}>
                  {window.relTime(s.created_at)}
                </td>
                <td>
                  <div style={{ display:'flex', gap: 6, justifyContent:'flex-end' }}>
                    {s.status === 'pending' && (
                      <>
                        <window.Btn size="xs" kind="success" icon={<window.I.check size={11}/>}
                                    onClick={(e) => { e.stopPropagation(); doApprove(s.id); }}>通过</window.Btn>
                        <window.Btn size="xs" kind="danger" icon={<window.I.x size={11}/>}
                                    onClick={(e) => { e.stopPropagation(); setRejecting(s); }}>驳回</window.Btn>
                      </>
                    )}
                    <window.Btn size="xs" icon={<window.I.eye size={11}/>}
                                onClick={(e) => { e.stopPropagation(); setOpenId(s.id); }}>详情</window.Btn>
                  </div>
                </td>
              </tr>
            ))}
            {list.length === 0 && (
              <tr><td colSpan={10}>
                <window.Empty title="没有匹配的提交" hint="试试切换过滤器,或清空搜索关键词"/>
              </td></tr>
            )}
          </tbody>
        </table>
      </div>

      {/* Detail drawer */}
      {open && (
        <>
          <div className="drawer-backdrop" onClick={() => setOpenId(null)}/>
          <div className="drawer wide">
            <SubmissionDetail sub={open} onClose={() => setOpenId(null)}
                              onApprove={async () => { await doApprove(open.id); setOpenId(null); }}
                              onReject={() => setRejecting(open)}/>
          </div>
        </>
      )}

      {/* Reject dialog */}
      {rejecting && (
        <div className="drawer-backdrop" onClick={() => setRejecting(null)} style={{ display:'grid', placeItems:'center' }}>
          <div onClick={(e) => e.stopPropagation()} className="card" style={{ width: 480, animation: 'pop .14s ease' }}>
            <div className="card-header">
              <div className="card-title">驳回 #{rejecting.id}</div>
              <button className="icon-btn" onClick={() => setRejecting(null)}><window.I.x size={14}/></button>
            </div>
            <div className="card-pad">
              <label className="field">驳回原因(将通知提交者)</label>
              <textarea className="textarea" placeholder="说明原因,如:阵容与现有数据重复 / 字段不完整 / 配招不合理…"
                        value={rejectReason} onChange={(e) => setRejectReason(e.target.value)} autoFocus/>
            </div>
            <div className="card-pad" style={{ paddingTop: 0, display:'flex', justifyContent:'flex-end', gap: 8 }}>
              <window.Btn onClick={() => setRejecting(null)}>取消</window.Btn>
              <window.Btn kind="primary" onClick={async () => {
                const id = rejecting.id;
                await doReject(id, rejectReason);
                setRejecting(null); setRejectReason(''); setOpenId(null);
              }}>确认驳回</window.Btn>
            </div>
          </div>
        </div>
      )}

      {batchReject && (
        <div className="drawer-backdrop" onClick={() => setBatchReject(false)} style={{ display:'grid', placeItems:'center' }}>
          <div onClick={(e) => e.stopPropagation()} className="card" style={{ width: 480 }}>
            <div className="card-header"><div className="card-title">批量驳回 {selected.size} 条</div></div>
            <div className="card-pad">
              <label className="field">统一原因</label>
              <textarea className="textarea" placeholder="将应用到所有选中项…"/>
            </div>
            <div className="card-pad" style={{ paddingTop: 0, display:'flex', justifyContent:'flex-end', gap: 8 }}>
              <window.Btn onClick={() => setBatchReject(false)}>取消</window.Btn>
              <window.Btn kind="primary" onClick={async () => {
                const ids = Array.from(selected);
                await Promise.all(ids.map((id) => window.API.reject(id, '批量驳回').catch(()=>null)));
                toast.push(`已批量驳回 ${ids.length} 条`);
                setBatchReject(false); setSelected(new Set()); reload();
              }}>确认</window.Btn>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

function FilterTabs({ value, onChange, options }) {
  return (
    <div style={{ display:'inline-flex', background: 'var(--surface-2)', border: '1px solid var(--border)',
                  borderRadius: 'var(--r-md)', padding: 3 }}>
      {options.map((o) => (
        <button key={o.v}
                onClick={() => onChange(o.v)}
                style={{
                  padding: '5px 12px',
                  fontSize: 13, fontWeight: 500,
                  borderRadius: 'var(--r-sm)',
                  color: value === o.v ? 'var(--text)' : 'var(--text-2)',
                  background: value === o.v ? 'var(--surface)' : 'transparent',
                  boxShadow: value === o.v ? 'var(--shadow-sm)' : 'none',
                  border: value === o.v ? '1px solid var(--border)' : '1px solid transparent',
                }}>
          {o.label}
          <span style={{ color: 'var(--text-3)', marginLeft: 6, fontFamily:'var(--font-mono)', fontSize: 11 }}>{o.count}</span>
        </button>
      ))}
    </div>
  );
}

function Chip({ children }) {
  return (
    <span style={{
      display:'inline-flex', alignItems:'center',
      fontSize: 11, padding: '2px 7px',
      background: 'var(--surface-2)', border: '1px solid var(--border)',
      borderRadius: 'var(--r-sm)',
      color: 'var(--text-2)',
      whiteSpace:'nowrap',
    }}>{children}</span>
  );
}

// ────────────────────────────────────────────────────────────────────
// Compare view: existing simple_match team vs new submission
function SubmissionDetail({ sub, onClose, onApprove, onReject }) {
  const key = `${sub.area_code}::${sub.npc}`;
  const currentTeams = window.MOCK.CURRENT_TEAMS[key] || [];
  const newToArea = currentTeams.length === 0;
  const [pickedExisting, setPickedExisting] = React.useState(0);
  const current = currentTeams[pickedExisting];

  return (
    <>
      <div className="drawer-header">
        <div>
          <div className="hstack" style={{ gap: 10 }}>
            <span className="mono" style={{ fontSize: 13, color: 'var(--text-3)' }}>#{sub.id}</span>
            <window.Pill status={sub.status}/>
            {newToArea && <span className="pill new"><span className="dot"/>新增天王</span>}
            <span className="muted" style={{ fontSize: 13 }}>·</span>
            <span style={{ fontWeight: 600 }}>{sub.area_code} / {sub.npc}</span>
          </div>
          <div className="muted" style={{ fontSize: 12, marginTop: 6 }}>
            提交于 {window.relTime(sub.created_at)} · 提交者 <span className="mono">{sub.submitter}</span>
          </div>
        </div>
        <div className="hstack">
          <window.Btn size="sm" icon={<window.I.chevL size={12}/>}>上一条</window.Btn>
          <window.Btn size="sm">下一条 <window.I.chevR size={12}/></window.Btn>
          <button className="icon-btn" onClick={onClose}><window.I.x size={16}/></button>
        </div>
      </div>

      <div className="drawer-body">
        {/* Alerts */}
        {newToArea && (
          <div className="alert info" style={{ marginBottom: 16 }}>
            <window.I.info size={16}/>
            <div>
              <div style={{ fontWeight: 500 }}>这是该区域下从未出现过的 NPC。</div>
              <div style={{ marginTop: 2 }}>通过后将自动在 <span className="mono">{sub.area_code}</span> 创建 <b>{sub.npc}</b> 节点。</div>
            </div>
          </div>
        )}
        {!newToArea && (
          <div className="alert warn" style={{ marginBottom: 16 }}>
            <window.I.alert size={16}/>
            <div>
              <div style={{ fontWeight: 500 }}>提交者 {sub.submitter} 近 24 小时已提交 3 条,通过率 67%。</div>
              <div style={{ marginTop: 2 }}>注意核对是否为同一阵容的多次提交。</div>
            </div>
          </div>
        )}

        {/* Picker for which "existing" to compare against */}
        {currentTeams.length > 1 && (
          <div className="hstack" style={{ marginBottom: 12 }}>
            <span className="muted" style={{ fontSize: 12 }}>对比目标:</span>
            {currentTeams.map((t, i) => (
              <button key={i} onClick={() => setPickedExisting(i)} className={`btn xs ${pickedExisting === i ? 'primary' : ''}`}>
                {t.idx}
              </button>
            ))}
          </div>
        )}

        {/* Compare grid */}
        <div className="compare-grid">
          <ComparePanel side="existing" label={current ? `现有 · ${current.idx}` : '现有 · 无'}
                        empty={!current}
                        team={current?.team || []}
                        meta={current ? {
                          speed: current.speed, drug: current.drug, weather: current.weather,
                          ding: current.ding, lock: current.lock, chu: current.chu, note: current.note,
                        } : null}/>
          <ComparePanel side="new" label="新提交"
                        team={sub.team}
                        compareTo={current?.team}
                        meta={{
                          speed: sub.speed, drug: sub.drug, weather: sub.weather,
                          ding: sub.ding, lock: sub.lock, chu: sub.chu, note: sub.note,
                        }}
                        compareMeta={current ? {
                          speed: current.speed, drug: current.drug, weather: current.weather,
                          ding: current.ding, lock: current.lock, chu: current.chu,
                        } : null}/>
        </div>

        {/* Submitter history */}
        <div className="section-head"><div className="h">提交者历史<span className="count">(近 7 天 3 条)</span></div></div>
        <div className="card" style={{ padding: 0 }}>
          <table className="table">
            <tbody>
              {[247, 245, 241].map((id) => {
                const s = window.MOCK.SUBMISSIONS.find((x) => x.id === id);
                if (!s) return null;
                return (
                  <tr key={id}>
                    <td className="mono" style={{ width: 60, color: 'var(--text-3)' }}>#{s.id}</td>
                    <td style={{ width: 120 }}>{s.area_code} / {s.npc}</td>
                    <td><window.Pill status={s.status}/></td>
                    <td className="muted">{window.relTime(s.created_at)}</td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>

      <div className="drawer-footer">
        <div className="hstack" style={{ fontSize: 12, color: 'var(--text-3)' }}>
          <window.KBD>A</window.KBD> 通过
          <window.KBD>R</window.KBD> 驳回
          <window.KBD>Esc</window.KBD> 关闭
        </div>
        {sub.status === 'pending' ? (
          <div className="hstack">
            <window.Btn kind="danger" icon={<window.I.x size={14}/>} onClick={onReject}>驳回</window.Btn>
            <window.Btn kind="primary" icon={<window.I.check size={14}/>} onClick={onApprove}>通过并合并</window.Btn>
          </div>
        ) : (
          <div className="muted" style={{ fontSize: 13 }}>
            已 {sub.status === 'approved' ? '通过' : sub.status === 'rejected' ? '驳回' : '合并'}
            {sub.reject_reason && <> · 原因: {sub.reject_reason}</>}
          </div>
        )}
      </div>
    </>
  );
}

function ComparePanel({ side, label, team, meta, compareTo, compareMeta, empty }) {
  const isNew = side === 'new';
  return (
    <div className="card" style={{
      borderColor: isNew ? 'var(--indigo)' : 'var(--border)',
      boxShadow: isNew ? '0 0 0 1px var(--indigo)' : 'none'
    }}>
      <div className="card-header" style={{
        background: isNew ? 'var(--primary-50)' : 'var(--surface-2)',
        borderBottom: '1px solid var(--border)',
      }}>
        <div className="card-title">
          <span style={{
            display:'inline-block', width:8, height:8, borderRadius:'50%',
            background: isNew ? 'var(--indigo)' : 'var(--text-3)', marginRight: 8,
          }}/>
          {label}
        </div>
        {!empty && <span className="mono" style={{ fontSize: 11, color: 'var(--text-3)' }}>6 只</span>}
      </div>
      {empty ? (
        <window.Empty title="该 NPC 下还没有阵容" hint="通过后会作为第 1 套创建"/>
      ) : (
        <div className="card-pad-sm">
          {/* Team row */}
          <div className="team-row">
            {team.map((p, i) => {
              const cmp = compareTo?.[i];
              const changed = cmp && cmp.name !== p.name;
              return (
                <div key={i} style={{ textAlign:'center', position:'relative' }}>
                  <div style={{ position:'relative', display:'inline-block' }}>
                    <window.Sprite name={p.name} color={p.color} size="lg"/>
                    {changed && (
                      <span style={{
                        position:'absolute', top: -6, right: -6,
                        width: 18, height: 18, borderRadius: '50%',
                        background: 'var(--warning)', color: 'white',
                        fontSize: 10, fontWeight: 600,
                        display:'grid', placeItems:'center',
                        border: '2px solid var(--surface)'
                      }}>Δ</span>
                    )}
                  </div>
                  <div style={{ fontSize: 12, marginTop: 6, fontWeight: 500 }}
                       className={changed ? 'diff-changed' : ''}>{p.name}</div>
                  <div style={{ display:'flex', justifyContent:'center', gap: 3, marginTop: 4, flexWrap:'wrap' }}>
                    {p.types.map((t) => <window.TypeChip key={t} type={t}/>)}
                  </div>
                </div>
              );
            })}
          </div>

          {/* Meta */}
          <div className="divider"/>
          <div style={{ display:'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 8, fontSize: 13 }}>
            <MetaRow label="速度" v={meta.speed} cv={compareMeta?.speed}/>
            <MetaRow label="药水" v={meta.drug} cv={compareMeta?.drug}/>
            <MetaRow label="天气" v={meta.weather} cv={compareMeta?.weather}/>
            <MetaRow label="开局" v={[meta.ding && '定', meta.lock && '锁', meta.chu && '初'].filter(Boolean).join(' / ') || '—'}
                     cv={compareMeta ? [compareMeta.ding && '定', compareMeta.lock && '锁', compareMeta.chu && '初'].filter(Boolean).join(' / ') || '—' : null}/>
          </div>
          {meta.note && (
            <>
              <div className="divider"/>
              <div className="muted" style={{ fontSize: 12, marginBottom: 4 }}>提交者备注</div>
              <div style={{ fontSize: 13, lineHeight: 1.5 }}>{meta.note}</div>
            </>
          )}
        </div>
      )}
    </div>
  );
}

function MetaRow({ label, v, cv }) {
  const changed = cv != null && cv !== v;
  return (
    <div className="hstack" style={{ justifyContent:'space-between' }}>
      <span className="muted" style={{ fontSize: 12 }}>{label}</span>
      <span style={{ fontWeight: 500 }} className={changed ? 'diff-changed' : ''}>{v}</span>
    </div>
  );
}

Object.assign(window, { SubmissionsPage });
