// ── Market View ──────────────────────────────────────

function PctCell({v}) {
  if (v == null || v === '') return <span className="text-zinc-600">—</span>;
  const n = typeof v === 'string' ? parseFloat(v) : v;
  if (isNaN(n)) return <span className="text-zinc-500">{v}</span>;
  const color = n > 0 ? 'text-red-400' : n < 0 ? 'text-emerald-400' : 'text-zinc-400';
  return <span className={`font-medium tabular-nums ${color}`}>{n > 0 ? '+' : ''}{n.toFixed(2)}%</span>;
}

function StatCard({icon, label, value, sub, color}) {
  return (
    <div className="bg-surface-2 rounded-xl border border-border p-4 flex items-center gap-3">
      <span className="text-2xl">{icon}</span>
      <div className="flex-1 min-w-0">
        <div className="text-[11px] text-zinc-500 uppercase tracking-wider">{label}</div>
        <div className={`text-xl font-bold tabular-nums ${color || 'text-white'}`}>{value}</div>
        {sub && <div className="text-[11px] text-zinc-600 truncate">{sub}</div>}
      </div>
    </div>
  );
}

function MarketView() {
  const [overview, setOverview] = useState(null);
  const [loading, setLoading] = useState(true);
  const [tab, setTab] = useState('zt');
  const [question, setQuestion] = useState('');
  const [answer, setAnswer] = useState('');
  const [answerLoading, setAnswerLoading] = useState(false);
  const toast = useContext(ToastContext);

  const load = async () => {
    setLoading(true);
    try {
      const r = await apiGet('/api/market/overview');
      if (r && !r.error) setOverview(r);
    } catch(e) { toast('行情加载失败', 'error'); }
    setLoading(false);
  };

  useEffect(() => { load(); const iv = setInterval(load, 60000); return () => clearInterval(iv); }, []);

  const askQ = async () => {
    if (!question.trim()) return;
    setAnswerLoading(true); setAnswer('');
    const r = await apiPost('/api/command', {cmd:'ask', args:question});
    setAnswer(r.result!=null ? String(r.result) : '');
    setAnswerLoading(false);
    if (r.error) toast('分析失败', 'error');
  };

  if (loading && !overview) return <div className="flex-1 flex items-center justify-center"><Spinner size={8} /></div>;

  const ctx = overview?.time_context || {};
  const stats = overview?.stats || {};
  const ztList = overview?.limitup || [];
  const lbList = overview?.limitstep || [];
  const bkList = overview?.concepts || [];
  const hotList = overview?.hot || [];
  const idxList = overview?.indices || [];
  const topInd = stats.top_industries || [];

  const tabs = [
    {id:'zt', label:'涨停板', icon:'🔴', badge: stats.zt_count},
    {id:'lb', label:'连板股', icon:'🔗', badge: lbList.length},
    {id:'bk', label:'板块', icon:'🏷️', badge: bkList.length},
    {id:'hot', label:'热股', icon:'🔥', badge: hotList.length},
  ];

  return (
    <div className="flex-1 overflow-y-auto overflow-x-hidden p-6 space-y-4 animate-fade-in">
      <div className="flex items-center justify-between">
        <div>
          <h1 className="text-xl font-bold text-white">行情总览</h1>
          <p className="text-[12px] text-zinc-500 mt-0.5">{ctx.freshness_label || ''} · {ctx.phase_cn || ''} · {ctx.timestamp || ''}</p>
        </div>
        <button onClick={load} disabled={loading} className="btn px-3 py-1.5 bg-surface-2 hover:bg-surface-3 rounded-lg text-[12px] text-zinc-400 border border-border hover:border-border-light transition disabled:opacity-50">{loading ? <Spinner /> : '刷新'}</button>
      </div>

      {/* 统计卡片 */}
      <div className="grid grid-cols-2 md:grid-cols-4 gap-3">
        <StatCard icon="🔴" label="涨停" value={stats.zt_count || 0} sub={topInd.length > 0 ? topInd.map(([n,c])=>`${n}×${c}`).slice(0,3).join(' ') : ''} color="text-red-400" />
        <StatCard icon="🔗" label="最高连板" value={stats.lb_max_height || 0} sub={lbList[0] ? `${lbList[0].name} ${lbList[0].up_stat||''}` : ''} color="text-amber-400" />
        <StatCard icon="🏷️" label="领涨板块" value={bkList[0]?.board_name || '—'} sub={bkList[0] ? `${bkList[0].pct_chg||0}% 涨${bkList[0].up_count||0}跌${bkList[0].down_count||0}` : ''} color="text-brand-400" />
        <StatCard icon="🔥" label="人气龙头" value={hotList[0]?.ts_name || '—'} sub={hotList[0] ? `#${hotList[0].rank||1} ${hotList[0].pct_change||''}%` : ''} color="text-orange-400" />
      </div>

      {/* 大盘指数 */}
      {idxList.length > 0 && (
        <div className="flex flex-wrap gap-2">
          {idxList.map((idx, i) => {
            const pct = parseFloat(idx.pct_chg || idx.pct_change || 0);
            const color = pct > 0 ? 'border-red-900/40 bg-red-950/20' : pct < 0 ? 'border-emerald-900/40 bg-emerald-950/20' : 'border-border bg-surface-2';
            return (
              <div key={i} className={`px-3 py-2 rounded-xl border ${color} text-center min-w-[120px]`}>
                <div className="text-[11px] text-zinc-500 truncate">{idx.name || idx.ts_name || idx.index_name || ''}</div>
                <div className="text-sm font-bold text-zinc-200 tabular-nums">{idx.close || idx.price || '—'}</div>
                <PctCell v={pct} />
              </div>
            );
          })}
        </div>
      )}

      {/* Tab 切换 */}
      <div className="flex gap-2 flex-wrap">
        {tabs.map(t => (
          <button key={t.id} onClick={() => setTab(t.id)}
            className={`btn flex items-center gap-1.5 px-4 py-2 rounded-xl text-[13px] font-medium border transition-all
              ${tab===t.id ? 'bg-brand-600/20 text-brand-400 border-brand-500/30' : 'bg-surface-2 text-zinc-400 border-border hover:border-border-light'}`}>
            <span>{t.icon}</span>
            <span>{t.label}</span>
            {t.badge > 0 && <span className={`text-[10px] px-1.5 py-0.5 rounded-md font-medium ${tab===t.id ? 'bg-brand-600/30 text-brand-300' : 'bg-surface-3 text-zinc-500'}`}>{t.badge}</span>}
          </button>
        ))}
      </div>

      {/* 数据表格区 */}
      <Card>
        {tab === 'zt' && (
          <div>
            <h3 className="text-sm font-semibold text-zinc-200 mb-3">🔴 涨停板 ({stats.zt_count || 0} 只)</h3>
            {ztList.length === 0 ? <div className="text-zinc-600 text-sm py-4 text-center">暂无涨停数据</div> : (
              <div className="overflow-x-auto">
                <table className="w-full min-w-0 text-[12px]">
                  <thead><tr className="text-zinc-500 border-b border-border/50">
                    <th className="text-left py-2 px-1 w-8">#</th>
                    <th className="text-left py-2 px-1">名称</th>
                    <th className="text-left py-2 px-1">代码</th>
                    <th className="text-right py-2 px-1">涨幅</th>
                    <th className="text-right py-2 px-1">封单(万)</th>
                    <th className="text-left py-2 px-1">行业</th>
                    <th className="text-center py-2 px-1">连板</th>
                  </tr></thead>
                  <tbody>{ztList.map((r, i) => {
                    const times = r.limit_times || 1;
                    const fd = Math.round((r.fd_amount || 0) / 10000);
                    return (
                      <tr key={i} className="border-b border-border/20 hover:bg-surface-3/50 transition">
                        <td className="py-2 px-1 text-zinc-600">{i+1}</td>
                        <td className="py-2 px-1 text-zinc-200 font-medium">{r.name || ''}</td>
                        <td className="py-2 px-1 text-zinc-500 font-mono">{r.ts_code || ''}</td>
                        <td className="py-2 px-1 text-right"><PctCell v={r.pct_chg} /></td>
                        <td className="py-2 px-1 text-right text-zinc-400 tabular-nums">{fd > 0 ? fd.toLocaleString() : '—'}</td>
                        <td className="py-2 px-1 text-zinc-500">{r.industry || ''}</td>
                        <td className="py-2 px-1 text-center">{times > 1 ? <span className="px-1.5 py-0.5 bg-red-950/40 text-red-400 rounded text-[10px] font-bold">{times}板</span> : <span className="text-zinc-700">首板</span>}</td>
                      </tr>
                    );
                  })}</tbody>
                </table>
              </div>
            )}
          </div>
        )}

        {tab === 'lb' && (
          <div>
            <h3 className="text-sm font-semibold text-zinc-200 mb-3">🔗 连板股</h3>
            {lbList.length === 0 ? <div className="text-zinc-600 text-sm py-4 text-center">暂无连板数据</div> : (
              <div className="overflow-x-auto">
                <table className="w-full min-w-0 text-[12px]">
                  <thead><tr className="text-zinc-500 border-b border-border/50">
                    <th className="text-left py-2 px-1 w-8">#</th>
                    <th className="text-left py-2 px-1">名称</th>
                    <th className="text-left py-2 px-1">代码</th>
                    <th className="text-right py-2 px-1">涨幅</th>
                    <th className="text-left py-2 px-1">连板</th>
                    <th className="text-left py-2 px-1">行业</th>
                  </tr></thead>
                  <tbody>{lbList.map((r, i) => (
                    <tr key={i} className="border-b border-border/20 hover:bg-surface-3/50 transition">
                      <td className="py-2 px-1 text-zinc-600">{i+1}</td>
                      <td className="py-2 px-1 text-zinc-200 font-medium">{r.name || ''}</td>
                      <td className="py-2 px-1 text-zinc-500 font-mono">{r.ts_code || ''}</td>
                      <td className="py-2 px-1 text-right"><PctCell v={r.pct_chg} /></td>
                      <td className="py-2 px-1"><span className="px-2 py-0.5 bg-amber-950/40 text-amber-400 rounded text-[10px] font-bold">{r.up_stat || r.limit_times || ''}</span></td>
                      <td className="py-2 px-1 text-zinc-500">{r.industry || ''}</td>
                    </tr>
                  ))}</tbody>
                </table>
              </div>
            )}
          </div>
        )}

        {tab === 'bk' && (
          <div>
            <h3 className="text-sm font-semibold text-zinc-200 mb-3">🏷️ 概念板块涨幅榜</h3>
            {bkList.length === 0 ? <div className="text-zinc-600 text-sm py-4 text-center">暂无板块数据</div> : (
              <div className="overflow-x-auto">
                <table className="w-full min-w-0 text-[12px]">
                  <thead><tr className="text-zinc-500 border-b border-border/50">
                    <th className="text-left py-2 px-1 w-8">#</th>
                    <th className="text-left py-2 px-1">板块</th>
                    <th className="text-right py-2 px-1">涨幅</th>
                    <th className="text-right py-2 px-1 text-emerald-600">涨</th>
                    <th className="text-right py-2 px-1 text-red-600">跌</th>
                    <th className="text-left py-2 px-1">领涨股</th>
                  </tr></thead>
                  <tbody>{bkList.map((r, i) => (
                    <tr key={i} className="border-b border-border/20 hover:bg-surface-3/50 transition">
                      <td className="py-2 px-1 text-zinc-600">{i+1}</td>
                      <td className="py-2 px-1 text-zinc-200 font-medium">{r.board_name || ''}</td>
                      <td className="py-2 px-1 text-right"><PctCell v={r.pct_chg} /></td>
                      <td className="py-2 px-1 text-right text-red-400 tabular-nums">{r.up_count || 0}</td>
                      <td className="py-2 px-1 text-right text-emerald-400 tabular-nums">{r.down_count || 0}</td>
                      <td className="py-2 px-1 text-zinc-400">{r.leading_stock_name || ''}</td>
                    </tr>
                  ))}</tbody>
                </table>
              </div>
            )}
          </div>
        )}

        {tab === 'hot' && (
          <div>
            <h3 className="text-sm font-semibold text-zinc-200 mb-3">🔥 同花顺热股</h3>
            {hotList.length === 0 ? <div className="text-zinc-600 text-sm py-4 text-center">暂无热股数据</div> : (
              <div className="overflow-x-auto">
                <table className="w-full min-w-0 text-[12px]">
                  <thead><tr className="text-zinc-500 border-b border-border/50">
                    <th className="text-center py-2 px-1 w-10">排名</th>
                    <th className="text-left py-2 px-1">名称</th>
                    <th className="text-left py-2 px-1">代码</th>
                    <th className="text-right py-2 px-1">涨跌</th>
                    <th className="text-left py-2 px-1">概念</th>
                  </tr></thead>
                  <tbody>{hotList.map((r, i) => {
                    let concept = '';
                    try { const tags = JSON.parse(r.concept || '[]'); concept = Array.isArray(tags) ? tags.slice(0,2).join(' ') : ''; } catch(e) { concept = r.concept || ''; }
                    return (
                      <tr key={i} className="border-b border-border/20 hover:bg-surface-3/50 transition">
                        <td className="py-2 px-1 text-center"><span className={`inline-block w-6 h-6 leading-6 rounded-lg text-[11px] font-bold ${i<3?'bg-orange-600/30 text-orange-300':'bg-surface-3 text-zinc-500'}`}>{r.rank || i+1}</span></td>
                        <td className="py-2 px-1 text-zinc-200 font-medium">{r.ts_name || ''}</td>
                        <td className="py-2 px-1 text-zinc-500 font-mono">{r.ts_code || ''}</td>
                        <td className="py-2 px-1 text-right"><PctCell v={r.pct_change} /></td>
                        <td className="py-2 px-1 text-zinc-600 text-[11px] truncate max-w-[180px]">{concept}</td>
                      </tr>
                    );
                  })}</tbody>
                </table>
              </div>
            )}
          </div>
        )}
      </Card>

      {/* 行业分布 */}
      {topInd.length > 0 && (
        <Card>
          <h3 className="text-sm font-semibold text-zinc-200 mb-3">🏭 涨停行业分布</h3>
          <div className="flex flex-wrap gap-2">
            {topInd.map(([name, count], i) => (
              <div key={i} className="flex items-center gap-1.5 px-3 py-1.5 bg-surface-3 rounded-lg border border-border/50">
                <span className="text-[12px] text-zinc-300">{name}</span>
                <span className="text-[11px] font-bold text-red-400 bg-red-950/40 px-1.5 py-0.5 rounded">{count}</span>
              </div>
            ))}
          </div>
        </Card>
      )}

      {/* 自由分析 */}
      <Card>
        <h3 className="text-sm font-semibold text-zinc-300 mb-3">🔬 AI 自由分析</h3>
        <div className="flex gap-2">
          <input value={question} onChange={e=>setQuestion(e.target.value)}
            onKeyDown={e=>e.key==='Enter'&&askQ()}
            placeholder="输入关于市场的问题，AI 会结合实时数据分析..."
            className="flex-1 bg-surface-3 border border-border rounded-xl px-4 py-2.5 text-sm text-zinc-200 focus:outline-none focus:border-brand-500/40 transition placeholder-zinc-600" />
          <button onClick={askQ} disabled={answerLoading}
            className="btn px-5 py-2.5 bg-brand-600 hover:bg-brand-700 rounded-xl text-sm text-white font-medium transition disabled:opacity-50 shadow-lg shadow-brand-600/20">
            {answerLoading ? <Spinner /> : '分析'}
          </button>
        </div>
        {(answer || answerLoading) && (
          <div className="mt-3 pt-3 border-t border-border">
            <DataBlock data={answer} loading={answerLoading} />
          </div>
        )}
      </Card>
    </div>
  );
}


// ── Tasks View ───────────────────────────────────────

function TaskStepRow({step, index}) {
  const icons = {pending:'⬜',running:'🔵',completed:'✅',failed:'❌',skipped:'⏭️'};
  const elapsed = (step.finished_at && step.started_at) ? ((step.finished_at - step.started_at).toFixed(1) + 's') : '';
  return (
    <div className={`flex items-center gap-2 py-1.5 text-[12px] ${step.status==='running' ? 'text-white' : 'text-zinc-400'}`}>
      <span className="w-4 text-center">{icons[step.status]||'❓'}</span>
      <span className="text-zinc-600 w-5 text-right">{index+1}.</span>
      <span className="flex-1 truncate">{step.description}</span>
      <span className="text-[10px] text-zinc-600 font-mono">{step.agent}.{step.action}</span>
      {elapsed && <span className="text-[10px] text-zinc-600 tabular-nums">{elapsed}</span>}
      {step.error && <span className="text-[10px] text-red-400 truncate max-w-[200px]" title={step.error}>❗{step.error.slice(0,40)}</span>}
    </div>
  );
}

function TaskCard({task, onCancel, onRefresh, expanded, onToggle}) {
  const icons = {pending:'⏳',running:'🔄',paused:'⏸️',completed:'✅',failed:'❌',cancelled:'🚫'};
  const colors = {pending:'border-zinc-700',running:'border-brand-500/40 ring-1 ring-brand-500/10',completed:'border-emerald-500/30',failed:'border-red-500/30',cancelled:'border-zinc-700'};
  const elapsed = task.elapsed ? (task.elapsed < 60 ? task.elapsed.toFixed(0)+'s' : (task.elapsed/60).toFixed(1)+'min') : '';
  const running = task.status === 'running';
  return (
    <Card className={`${colors[task.status]||'border-border'} ${running ? 'glow-brand' : ''}`}>
      <div className="flex items-center gap-3 cursor-pointer" onClick={onToggle}>
        <span className="text-lg">{icons[task.status]||'❓'}</span>
        <div className="flex-1 min-w-0">
          <div className="flex items-center gap-2">
            <span className="text-sm font-semibold text-white truncate">{task.name}</span>
            <span className={`text-[10px] px-1.5 py-0.5 rounded font-medium ${running ? 'bg-brand-600/20 text-brand-400' : 'bg-surface-3 text-zinc-500'}`}>{task.status}</span>
          </div>
          <div className="flex items-center gap-3 mt-1 text-[10px] text-zinc-500">
            <span>{task.id}</span>
            {elapsed && <span>⏱ {elapsed}</span>}
            <span>{task.steps?.length||0} 步骤</span>
          </div>
        </div>
        <div className="flex items-center gap-2 flex-shrink-0">
          <div className="w-24 bg-surface-3 rounded-full h-1.5 overflow-hidden">
            <div className={`h-full rounded-full transition-all duration-500 ${task.status==='completed' ? 'bg-emerald-500' : task.status==='failed' ? 'bg-red-500' : 'bg-brand-500'}`} style={{width: `${task.progress||0}%`}}></div>
          </div>
          <span className="text-[11px] text-zinc-400 tabular-nums w-8 text-right">{Math.round(task.progress||0)}%</span>
        </div>
      </div>
      {expanded && (
        <div className="mt-3 pt-3 border-t border-border/50 space-y-0.5">
          {(task.steps||[]).map((s,i) => <TaskStepRow key={i} step={s} index={i} />)}
          <div className="flex gap-2 mt-3 pt-2 border-t border-border/30">
            {(task.status==='running'||task.status==='pending') && (
              <button onClick={(e)=>{e.stopPropagation();onCancel(task.id);}} className="btn px-3 py-1.5 text-[11px] bg-red-950/40 hover:bg-red-900/40 text-red-400 rounded-lg border border-red-900/30 transition">取消任务</button>
            )}
            <button onClick={(e)=>{e.stopPropagation();onRefresh();}} className="btn px-3 py-1.5 text-[11px] bg-surface-3 hover:bg-surface-4 text-zinc-400 rounded-lg border border-border transition">刷新</button>
          </div>
        </div>
      )}
    </Card>
  );
}

function TasksView() {
  const [tasks, setTasks] = useState([]);
  const [presets, setPresets] = useState({});
  const [listLoading, setListLoading] = useState(false);
  const [creating, setCreating] = useState('');
  const [expandedId, setExpandedId] = useState('');
  const [progressLog, setProgressLog] = useState({});
  const toast = useContext(ToastContext);
  const sseRefs = useRef({});

  const refresh = async () => {
    setListLoading(true);
    try {
      const r = await apiGet('/api/tasks');
      if (r && !r.error) { if (r.tasks) setTasks(r.tasks); if (r.presets) setPresets(r.presets); }
    } catch(e) { toast('加载失败', 'error'); }
    setListLoading(false);
  };

  const create = async (preset) => {
    setCreating(preset);
    try {
      const r = await apiPost('/api/tasks/create', {preset});
      if (!r.error) {
        toast((presets[preset]?.name || preset) + ' 已创建', 'success');
        setTimeout(refresh, 500);
      } else toast('创建失败', 'error');
    } catch(e) { toast('网络错误', 'error'); }
    setCreating('');
  };

  const cancelTask = async (id) => {
    await apiPost('/api/tasks/'+id+'/cancel', {});
    toast('任务已取消', 'success');
    refresh();
  };

  const subscribeProgress = (taskId) => {
    if (sseRefs.current[taskId]) return;
    try {
      const es = new EventSource('/api/tasks/'+taskId+'/progress');
      sseRefs.current[taskId] = es;
      es.onmessage = (e) => {
        try {
          const d = JSON.parse(e.data);
          if (d.text) setProgressLog(prev => ({...prev, [taskId]: [...(prev[taskId]||[]), d.text].slice(-20)}));
          if (d.event === 'done' || d.status === 'completed' || d.status === 'failed' || d.status === 'cancelled') {
            es.close(); delete sseRefs.current[taskId]; refresh();
          }
        } catch(err) {}
      };
      es.onerror = () => { es.close(); delete sseRefs.current[taskId]; };
    } catch(e) {}
  };

  useEffect(() => { refresh(); const iv = setInterval(refresh, 15000); return () => clearInterval(iv); }, []);
  useEffect(() => {
    tasks.filter(t => t.status === 'running').forEach(t => subscribeProgress(t.id));
  }, [tasks]);
  useEffect(() => () => { Object.values(sseRefs.current).forEach(es => es.close()); }, []);

  const running = tasks.filter(t => t.status === 'running' || t.status === 'pending');
  const finished = tasks.filter(t => t.status !== 'running' && t.status !== 'pending');

  return (
    <div className="flex-1 overflow-y-auto overflow-x-hidden p-6 animate-fade-in">
      <div className="flex items-center justify-between mb-4">
        <div>
          <h1 className="text-xl font-bold text-white">多任务管理</h1>
          <p className="text-[12px] text-zinc-500 mt-0.5">并行任务编排 · 实时进度追踪 · 多 Agent 协同</p>
        </div>
        <div className="flex items-center gap-2">
          {running.length > 0 && <span className="text-[11px] px-2 py-1 bg-brand-600/20 text-brand-400 rounded-lg animate-pulse">{running.length} 运行中</span>}
          <button onClick={refresh} disabled={listLoading} className="btn px-3 py-1.5 rounded-lg text-[11px] bg-surface-2 text-zinc-400 border border-border hover:border-border-light transition disabled:opacity-50">{listLoading ? <Spinner /> : '刷新'}</button>
        </div>
      </div>


      <div className="grid md:grid-cols-[1fr_280px] gap-4">
        <div className="space-y-3">
          {running.length > 0 && (<div className="text-[11px] text-zinc-500 uppercase tracking-widest mb-1">⚡ 运行中</div>)}
          {running.map(t => (
            <div key={t.id}>
              <TaskCard task={t} onCancel={cancelTask} onRefresh={refresh} expanded={expandedId===t.id} onToggle={()=>setExpandedId(expandedId===t.id?'':t.id)} />
              {progressLog[t.id] && expandedId===t.id && (
                <div className="mt-1 ml-6 space-y-0.5 text-[10px] text-zinc-500 font-mono max-h-24 overflow-y-auto overflow-x-hidden">
                  {progressLog[t.id].map((l,i) => <div key={i}>{l}</div>)}
                </div>
              )}
            </div>
          ))}
          {finished.length > 0 && (<div className="text-[11px] text-zinc-500 uppercase tracking-widest mb-1 mt-4">📋 历史任务</div>)}
          {finished.slice(0, 15).map(t => (
            <TaskCard key={t.id} task={t} onCancel={cancelTask} onRefresh={refresh} expanded={expandedId===t.id} onToggle={()=>setExpandedId(expandedId===t.id?'':t.id)} />
          ))}
          {tasks.length === 0 && !listLoading && <Card><div className="text-center py-8 text-zinc-600 text-sm">暂无任务记录</div></Card>}
        </div>

        <div className="space-y-2">
          <div className="text-[11px] text-zinc-500 uppercase tracking-widest mb-1">🚀 快速启动</div>
          {PRESETS.map(p => (
            <button key={p.id} onClick={()=>create(p.id)} disabled={!!creating}
              className="w-full flex items-center gap-2.5 px-3 py-2.5 rounded-xl bg-surface-1 border border-border hover:border-brand-500/30 hover:bg-brand-950/10 text-left transition group disabled:opacity-50">
              <span className="text-lg">{p.icon}</span>
              <div className="flex-1 min-w-0">
                <div className="text-[12px] font-semibold text-zinc-200 group-hover:text-brand-400 transition truncate">{p.name}</div>
                <div className="text-[10px] text-zinc-600 truncate">{p.desc}</div>
              </div>
              {creating===p.id ? <Spinner /> : <span className="text-[10px] text-zinc-600">{p.steps}步</span>}
            </button>
          ))}
        </div>
      </div>
    </div>
  );
}


// ── Intraday / Quant / System / DailyLog / LLMConfig ──
// (preserved from original — full implementations below)

function IntradaySignalCard({sig}) {
  const typeColor = {
    '连板龙头': 'text-red-400 bg-red-500/10 border-red-500/20',
    '今日涨停': 'text-orange-400 bg-orange-500/10 border-orange-500/20',
    '热股异动': 'text-amber-400 bg-amber-500/10 border-amber-500/20',
  }[sig.type] || 'text-zinc-400 bg-surface-3 border-border';
  return (
    <div className="flex items-center gap-3 px-3 py-2.5 bg-surface-3 rounded-xl border border-border hover:border-border-light transition">
      <span className={`text-[10px] font-semibold px-2 py-0.5 rounded-full border ${typeColor}`}>{sig.type}</span>
      <span className="font-mono font-bold text-zinc-100 text-sm">{sig.ts_code || sig.code || '—'}</span>
      {sig.name && <span className="text-zinc-400 text-xs truncate max-w-[80px]">{sig.name}</span>}
      <span className="text-zinc-500 text-xs ml-auto truncate max-w-[120px]">{sig.detail || ''}</span>
    </div>
  );
}

function IntradayView() {
  const [status, setStatus] = useState(null);
  const [loading, setLoading] = useState(false);
  const [selectLoading, setSelectLoading] = useState(false);
  const [scanLoading, setScanLoading] = useState(false);
  const [liveLoading, setLiveLoading] = useState(false);
  const [monitorLoading, setMonitorLoading] = useState(false);
  const [strategy, setStrategy] = useState('');
  const [liveResult, setLiveResult] = useState(null);
  // Auto-push config
  const [pushCfg, setPushCfg] = useState(null);
  const [pushSaving, setPushSaving] = useState(false);
  const [pushInterval, setPushInterval] = useState(30);
  const toast = React.useContext(ToastContext);

  const loadStatus = async () => {
    setLoading(true);
    try {
      const r = await fetch('/api/intraday/status', { headers: authHeaders(), credentials: 'same-origin' });
      if (r.ok) setStatus(await r.json());
    } catch(e) {}
    setLoading(false);
  };

  const loadLatestSignals = async () => {
    try {
      const r = await fetch('/api/intraday/latest-signals', { headers: authHeaders(), credentials: 'same-origin' });
      if (r.ok) {
        const d = await r.json();
        // Always surface the payload so the UI can show either signals or
        // the "尚未扫描" hint. The back-end marks the no-cache case with
        // needs_scan=true.
        if (d.signals && d.signals.length > 0) setLiveResult(d);
        else if (d.needs_scan) setLiveResult({...d, signals: []});
      }
    } catch(e) {}
  };

  const loadPushCfg = async () => {
    try {
      const r = await fetch('/api/intraday/auto-push/config', { headers: authHeaders() });
      if (r.ok) {
        const d = await r.json();
        setPushCfg(d);
        setPushInterval(d.interval_minutes || 30);
      }
    } catch(e) {}
  };

  useEffect(() => {
    loadStatus();
    loadLatestSignals();
    loadPushCfg();
    const t = setInterval(() => { loadStatus(); loadPushCfg(); }, 30000);
    return () => clearInterval(t);
  }, []);

  const runSelect = async () => {
    setSelectLoading(true);
    try { await fetch('/api/intraday/select', { method: 'POST', headers: authHeaders(), credentials: 'same-origin', body: JSON.stringify({strategy}) }); }
    catch(e) {}
    setSelectLoading(false);
    setTimeout(loadStatus, 2000);
  };

  const runScan = async () => {
    setScanLoading(true);
    try { await fetch('/api/intraday/scan', { method: 'POST', headers: authHeaders(), credentials: 'same-origin', body: JSON.stringify({strategy_logic: strategy || '默认策略监控'}) }); }
    catch(e) {}
    setScanLoading(false);
    setTimeout(loadStatus, 2000);
  };

  const runLiveScan = async () => {
    setLiveLoading(true);
    try {
      const r = await fetch('/api/intraday/scan-and-push', { method: 'POST', headers: authHeaders(), credentials: 'same-origin', body: JSON.stringify({push: false}) });
      const d = await r.json();
      if (d.ok) {
        setLiveResult(d);
      } else {
        setLiveResult({ error: d.message || '获取失败，请确认市场Agent已连接且处于交易时段' });
      }
    } catch(e) { setLiveResult({ error: '请求失败: ' + e.message }); }
    setLiveLoading(false);
  };

  const toggleAutoPush = async () => {
    setPushSaving(true);
    try {
      const newEnabled = !pushCfg?.enabled;
      const r = await fetch('/api/intraday/auto-push/config', {
        method: 'POST', headers: authHeaders(),
        body: JSON.stringify({ enabled: newEnabled, interval_minutes: pushInterval })
      });
      const d = await r.json();
      if (d.ok) {
        setPushCfg(d.config);
        toast(newEnabled ? `✅ 自动推送已开启，每 ${pushInterval} 分钟推送一次` : '⏹ 自动推送已关闭', newEnabled ? 'success' : 'info');
      }
    } catch(e) { toast('保存失败', 'error'); }
    setPushSaving(false);
  };

  const saveInterval = async () => {
    setPushSaving(true);
    try {
      const r = await fetch('/api/intraday/auto-push/config', {
        method: 'POST', headers: authHeaders(),
        body: JSON.stringify({ interval_minutes: pushInterval })
      });
      const d = await r.json();
      if (d.ok) { setPushCfg(d.config); toast('推送间隔已保存', 'success'); }
    } catch(e) { toast('保存失败', 'error'); }
    setPushSaving(false);
  };

  const triggerNow = async () => {
    setPushSaving(true);
    try {
      await fetch('/api/intraday/auto-push/trigger', { method: 'POST', headers: authHeaders() });
      toast('已触发，60秒内将推送', 'success');
      setTimeout(loadPushCfg, 5000);
    } catch(e) { toast('触发失败', 'error'); }
    setPushSaving(false);
  };

  const pool = status?.pool || {};
  const signals = status?.recent_signals || [];
  const scans = status?.recent_scans || [];
  const liveSignals = liveResult?.signals || [];
  const isTradingHours = status?.is_trading_hours;
  const autoPushOn = pushCfg?.enabled;

  return (
    <div className="flex-1 h-full overflow-y-auto overflow-x-hidden p-6 space-y-5 animate-fade-in">
      {/* Header */}
      <div className="flex items-center justify-between">
        <div>
          <h2 className="text-xl font-semibold text-zinc-100 flex items-center gap-2">
            <span className="text-2xl">⚡</span> 盘中实时选股
          </h2>
          <p className="text-sm text-zinc-500 mt-0.5">实时涨停/连板/热股筛选 · 盘后因子选股 · DolphinDB 扫描</p>
        </div>
        <div className="flex items-center gap-2">
          <span className={`text-[11px] px-2.5 py-1 rounded-full border font-medium ${isTradingHours ? 'text-emerald-400 bg-emerald-500/10 border-emerald-500/25' : 'text-zinc-500 bg-surface-3 border-border'}`}>
            {isTradingHours ? '📈 交易时段' : '🌙 非交易时段'}
          </span>
          <button onClick={loadStatus} disabled={loading} className="btn px-3 py-1.5 bg-surface-3 text-zinc-300 text-sm rounded-lg border border-border hover:bg-surface-4">
            {loading ? '...' : '🔄'}
          </button>
        </div>
      </div>

      {/* Live Scan — primary action */}
      <div className="bg-surface-2 rounded-2xl border border-border p-5">
        <div className="flex items-center justify-between mb-3">
          <h3 className="text-[12px] font-semibold text-zinc-300 flex items-center gap-2">
            <span>🔥</span> 实时行情选股
            <span className="text-[10px] text-zinc-600 font-normal">涨停 · 连板龙头 · 热股异动</span>
          </h3>
          <button
            onClick={runLiveScan}
            disabled={liveLoading}
            className="flex items-center gap-1.5 px-4 py-1.5 bg-rose-600/15 border border-rose-500/25 text-[12px] text-rose-400 rounded-xl hover:bg-rose-600/25 transition disabled:opacity-40"
          >
            {liveLoading ? <><span className="animate-spin">⏳</span> 扫描中...</> : '⚡ 立即扫描'}
          </button>
        </div>

        {liveResult?.error ? (
          <div className="text-[11px] text-amber-500/80 bg-amber-500/5 border border-amber-500/20 rounded-xl px-4 py-3">
            ⚠️ {liveResult.error}
          </div>
        ) : liveSignals.length > 0 ? (
          <>
            <div className="flex items-center gap-4 text-[11px] text-zinc-500 mb-3 pb-2 border-b border-border">
              <span className="text-white font-bold text-base">{liveResult.total_signals ?? liveSignals.length}</span>
              <span>只候选</span>
              {liveResult.limitup_count > 0 && <span>涨停 {liveResult.limitup_count}</span>}
              {liveResult.limitstep_count > 0 && <span>连板 {liveResult.limitstep_count}</span>}
              {liveResult.hot_count > 0 && <span>热股 {liveResult.hot_count}</span>}
              {liveResult.ts && (
                <span className="ml-auto flex items-center gap-2">
                  <FreshnessBadge ts={liveResult.ts} fresh={120} slow={1800} />
                  <span>{new Date(liveResult.ts * 1000).toLocaleTimeString('zh-CN')}</span>
                </span>
              )}
              {liveResult.signal_date && !liveResult.ts && <span className="ml-auto">{liveResult.signal_date}</span>}
            </div>
            <div className="space-y-1.5">
              {liveSignals.slice(0, 25).map((sig, i) => <IntradaySignalCard key={i} sig={sig} />)}
              {liveSignals.length > 25 && <div className="text-center text-[11px] text-zinc-600 pt-1">+{liveSignals.length - 25} 只</div>}
            </div>
          </>
        ) : (
          <div className="text-center py-8 text-zinc-600 text-sm">
            {liveLoading ? '扫描中...' : (
              <div className="space-y-2">
                <div>{liveResult?.needs_scan ? '📭 尚未扫描' : '点击「立即扫描」获取实时行情信号'}</div>
                <div className="text-[11px] text-zinc-700">{liveResult?.hint || '需要 Market Agent 已连接 · 交易时段 09:25~15:05 数据最丰富'}</div>
              </div>
            )}
          </div>
        )}
      </div>

      {/* Auto-Push Config */}
      <div className={`rounded-2xl border p-5 transition-all ${autoPushOn ? 'bg-emerald-500/5 border-emerald-500/25' : 'bg-surface-2 border-border'}`}>
        <div className="flex items-center justify-between mb-4">
          <div>
            <h3 className="text-[12px] font-semibold text-zinc-300 flex items-center gap-2">
              <span>🔔</span> 自动推送
              {autoPushOn && <span className="text-[10px] text-emerald-400 bg-emerald-500/10 border border-emerald-500/20 px-2 py-0.5 rounded-full">运行中</span>}
            </h3>
            <p className="text-[10px] text-zinc-600 mt-0.5">交易时段内定时扫描 → 推送飞书/Telegram</p>
          </div>
          {/* Toggle */}
          <button
            onClick={toggleAutoPush}
            disabled={pushSaving}
            className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors disabled:opacity-50 ${autoPushOn ? 'bg-emerald-500' : 'bg-surface-4'}`}
          >
            <span className={`inline-block h-4 w-4 rounded-full bg-white shadow transition-transform ${autoPushOn ? 'translate-x-6' : 'translate-x-1'}`} />
          </button>
        </div>

        <div className="flex items-center gap-3 flex-wrap">
          {/* Interval selector */}
          <div className="flex items-center gap-2">
            <span className="text-[11px] text-zinc-500">每</span>
            <select
              value={pushInterval}
              onChange={e => setPushInterval(Number(e.target.value))}
              className="bg-surface-3 border border-border text-zinc-200 text-[11px] rounded-lg px-2 py-1 focus:outline-none focus:border-brand-500"
            >
              {[5, 10, 15, 20, 30, 45, 60].map(m => (
                <option key={m} value={m}>{m} 分钟</option>
              ))}
            </select>
            <span className="text-[11px] text-zinc-500">推送一次</span>
            {pushInterval !== (pushCfg?.interval_minutes || 30) && (
              <button onClick={saveInterval} disabled={pushSaving} className="text-[11px] px-2 py-1 bg-brand-600/20 border border-brand-500/30 text-brand-400 rounded-lg hover:bg-brand-600/30 transition disabled:opacity-40">
                保存
              </button>
            )}
          </div>

          {/* Trigger now */}
          <button
            onClick={triggerNow}
            disabled={pushSaving}
            className="flex items-center gap-1.5 px-3 py-1 bg-surface-3 border border-border text-[11px] text-zinc-400 rounded-xl hover:text-white hover:border-border-light transition disabled:opacity-40"
          >
            ▶ 立即推送一次
          </button>
        </div>

        {/* Status line */}
        {pushCfg && (
          <div className="mt-3 text-[10px] text-zinc-600 flex items-center gap-4">
            {autoPushOn && pushCfg.is_trading_hours && pushCfg.next_push_in > 0 && (
              <span className="text-emerald-600">下次推送: {Math.floor(pushCfg.next_push_in / 60)}分{pushCfg.next_push_in % 60}秒后</span>
            )}
            {autoPushOn && !pushCfg.is_trading_hours && (
              <span className="text-zinc-600">⏸ 等待交易时段 (09:25~15:05)</span>
            )}
            {pushCfg.last_push_ts > 0 && (
              <span>上次推送: {new Date(pushCfg.last_push_ts * 1000).toLocaleTimeString('zh-CN')}</span>
            )}
          </div>
        )}
      </div>

      {/* Pool-based System */}
      <div className="bg-surface-2 rounded-2xl border border-border p-5">
        <h3 className="text-[12px] font-semibold text-zinc-300 mb-3 flex items-center gap-2">
          <span>📋</span> 系统选股池
          <span className="text-[10px] text-zinc-600 font-normal">盘后 ClickHouse 因子选股 → Redis 存池 → DolphinDB 盘中扫描</span>
        </h3>
        {pool.count > 0 ? (
          <div>
            <div className="flex items-center gap-4 mb-3">
              <span className="text-2xl font-bold text-brand-400">{pool.count}</span>
              <span className="text-sm text-zinc-500">只目标股</span>
              <span className="text-xs text-zinc-600 ml-auto">策略: {pool.strategy || '-'} | {pool.date || '-'}</span>
            </div>
            <div className="flex flex-wrap gap-1.5">
              {(pool.stocks || []).slice(0, 20).map(s => (
                <span key={s} className="px-2 py-0.5 bg-surface-3 text-xs text-zinc-400 rounded-lg border border-border">{s}</span>
              ))}
              {pool.count > 20 && <span className="text-xs text-zinc-600 px-2 py-0.5">+{pool.count - 20} 只</span>}
            </div>
          </div>
        ) : (
          <p className="text-zinc-600 text-sm">暂无目标股票池 — 运行「盘后选股」后生成</p>
        )}

        <div className="mt-4 pt-3 border-t border-border space-y-3">
          <input
            value={strategy}
            onChange={e => setStrategy(e.target.value)}
            placeholder="策略描述（可选）"
            className="w-full px-3 py-2 bg-surface-3 border border-border rounded-xl text-sm text-zinc-200 placeholder-zinc-600 focus:outline-none focus:border-brand-500"
          />
          <div className="flex gap-2 flex-wrap">
            <button onClick={runSelect} disabled={selectLoading} className="btn px-4 py-2 bg-brand-600 text-white text-sm rounded-xl hover:bg-brand-500 disabled:opacity-50">
              {selectLoading ? '⏳ 选股中...' : '🌙 盘后选股'}
            </button>
            <button onClick={runScan} disabled={scanLoading || !pool.count} className="btn px-4 py-2 bg-emerald-600/80 text-white text-sm rounded-xl hover:bg-emerald-500 disabled:opacity-50" title={!pool.count ? '需先运行盘后选股建立股池' : ''}>
              {scanLoading ? '⏳ 扫描中...' : '🔍 单次扫描'}
            </button>
            <button
              onClick={async () => {
                setMonitorLoading(true);
                try {
                  const endpoint = status?.monitoring ? '/api/intraday/stop' : '/api/intraday/monitor';
                  await fetch(endpoint, { method: 'POST', headers: authHeaders(), credentials: 'same-origin', body: JSON.stringify({strategy_logic: strategy}) });
                } catch(e) {}
                setMonitorLoading(false);
                setTimeout(loadStatus, 2000);
              }}
              disabled={monitorLoading || (!status?.monitoring && !pool.count)}
              className={`btn px-4 py-2 text-white text-sm rounded-xl disabled:opacity-50 ${status?.monitoring ? 'bg-red-600 hover:bg-red-500' : 'bg-amber-600/80 hover:bg-amber-500'}`}
            >
              {monitorLoading ? '⏳ ...' : (status?.monitoring ? '⏹ 停止监控' : '🔄 持续监控')}
            </button>
          </div>
          {status?.monitoring && <p className="text-xs text-emerald-400">🟢 自动监控运行中 — 每 {status?.auto_scan_interval || 120}s 扫描一次</p>}
          {!pool.count && <p className="text-[11px] text-zinc-600">提示: 「单次扫描」和「持续监控」需先运行盘后选股建立股票池</p>}
        </div>
      </div>

      {/* DolphinDB Signals */}
      {signals.length > 0 && (
        <div className="bg-surface-2 rounded-2xl border border-border p-5">
          <h3 className="text-[12px] font-semibold text-zinc-300 mb-3 flex items-center gap-2"><span>🎯</span> DolphinDB 信号</h3>
          <div className="space-y-1.5">
            {signals.slice(0, 10).map((sigGroup, i) => {
              const items = Array.isArray(sigGroup) ? sigGroup : [sigGroup];
              return items.map((sig, j) => (
                <div key={`${i}-${j}`} className="flex items-center gap-3 px-3 py-2 bg-surface-3 rounded-xl text-sm">
                  <span className={`font-medium ${sig.signal === 'BUY' ? 'text-red-400' : 'text-emerald-400'}`}>{sig.signal || 'SIG'}</span>
                  <span className="text-zinc-200 font-mono">{sig.ts_code || '-'}</span>
                  <span className="text-zinc-400">@ {sig.price || '-'}</span>
                  <span className="text-zinc-500 text-xs ml-auto truncate max-w-[160px]">{sig.reason || ''}</span>
                </div>
              ));
            })}
          </div>
        </div>
      )}

      {/* Scan Logs (compact) */}
      {scans.length > 0 && (
        <div className="bg-surface-2 rounded-2xl border border-border p-4">
          <h3 className="text-[11px] font-semibold text-zinc-500 mb-2">📜 扫描日志</h3>
          <div className="space-y-1">
            {scans.slice(0, 8).map((log, i) => (
              <div key={i} className="flex items-center gap-3 text-[11px] text-zinc-500">
                <span className="font-mono text-zinc-600">{log.timestamp ? log.timestamp.split('T')[1]?.split('.')[0] : '-'}</span>
                <span>扫描 {log.scanned || 0} 只</span>
                <span className={log.signals?.length > 0 ? 'text-red-400 font-medium' : 'text-zinc-700'}>
                  {log.signals?.length > 0 ? `🎯 ${log.signals.length} 只命中` : '无信号'}
                </span>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}
