// ===== page-upload.jsx =====
const { useState: useSU, useEffect: useEU, useRef: useRU, useCallback: useCBU } = React;

function UploadPage({ navigate }) {
  // States: idle → uploading → parsing → prescan → done → error
  const [stage, setStage]       = useSU('idle');
  const [file, setFile]         = useSU(null);
  const [progress, setProgress] = useSU(0);
  const [dragging, setDragging] = useSU(false);
  const [orderData, setOrderData] = useSU(null);   // 来自后端的真实订单
  const [error, setError]       = useSU(null);

  const meta = orderData && {
    name: orderData.file_name,
    sizeKb: file ? Math.round(file.size / 1024) : 0,
    words: orderData.word_count,
    estSec: 25,
    plan: orderData.plan === 'premium' ? `完整版 ¥${orderData.amount.toFixed(2)}` : `标准版 ¥${orderData.amount.toFixed(2)}`,
    orderId: orderData.order_id,
    amount: orderData.amount,
  };

  // 真实上传
  const beginUpload = useCBU(async (f) => {
    if (!f) return;
    setFile(f);
    setStage('uploading');
    setProgress(0);
    setError(null);

    try {
      const data = await window.API.uploadOrder(f);
      window.API.saveToken(data.order_id, data.access_token);
      setOrderData(data);
      // 等进度条跑完再切到 parsing 视觉阶段
    } catch (err) {
      console.error('upload failed', err);
      setError(err);
      setStage('error');
    }
  }, []);

  // 合成上传进度（fetch 没有原生上传进度，做视觉效果）
  useEU(() => {
    if (stage !== 'uploading') return;
    const id = setInterval(() => {
      setProgress(p => {
        // 没收到响应时停在 80% 左右等待
        const ceiling = orderData ? 100 : 85;
        if (p >= ceiling) return ceiling;
        return Math.min(ceiling, p + Math.random() * 12 + 4);
      });
    }, 220);
    return () => clearInterval(id);
  }, [stage, orderData]);

  // progress 达到 100 后转 parsing
  useEU(() => {
    if (stage === 'uploading' && progress >= 100 && orderData) {
      const t = setTimeout(() => setStage('parsing'), 250);
      return () => clearTimeout(t);
    }
  }, [stage, progress, orderData]);

  useEU(() => {
    if (stage !== 'parsing') return;
    const t = setTimeout(() => setStage('prescan'), 1800);
    return () => clearTimeout(t);
  }, [stage]);

  const reset = () => {
    setStage('idle'); setFile(null); setProgress(0); setOrderData(null); setError(null);
  };

  const goToOrder = () => {
    if (!orderData) return;
    // 把 access_token 一并写进 URL，使报告页可分享/可刷新/可换设备
    navigate('order', { id: orderData.order_id, token: orderData.access_token });
  };

  return (
    <div className="min-h-screen bg-ink-50 pt-12 pb-24">
      <div className="max-w-[1200px] mx-auto px-6 lg:px-8">
        <div className="max-w-[640px] mx-auto">
          <div className="flex items-center gap-2 text-[12.5px] text-ink-500 mb-6">
            <a onClick={() => navigate('home')} className="hover:text-navy-900 cursor-pointer">首页</a>
            <I.Chevron size={12} />
            <span className="text-navy-900">上传论文</span>
          </div>

          <h1 className="text-[32px] sm:text-[36px] font-bold text-navy-900 tracking-tight">上传你的论文</h1>
          <p className="mt-2 text-[14.5px] text-ink-500">支持 .docx / .pdf / .txt 单文件 ≤ 10MB · 仅用于本次检测，24 小时内自动删除</p>

          <Card className="mt-8 p-6 sm:p-8">
            {stage === 'idle' && (
              <DropZone dragging={dragging} setDragging={setDragging} onPick={beginUpload} />
            )}
            {stage === 'uploading' && (
              <UploadingPanel name={file?.name || '正在上传...'} progress={progress} />
            )}
            {stage === 'parsing' && (
              <ParsingPanel name={file?.name || meta?.name} />
            )}
            {stage === 'prescan' && meta && (
              <PrescanPanel meta={meta} onContinue={() => setStage('done')} />
            )}
            {stage === 'done' && meta && (
              <FileInfoCard meta={meta} onReplace={reset} onPay={goToOrder} />
            )}
            {stage === 'error' && (
              <ErrorPanel error={error} onRetry={reset} />
            )}
          </Card>

          <div className="mt-6 grid sm:grid-cols-3 gap-3">
            <TipChip icon={<I.Lock size={14} />} title="端到端加密" desc="HTTPS 传输 + 服务端加密存储" />
            <TipChip icon={<I.Clock size={14} />} title="24h 自动删除" desc="完成后从服务器彻底清除" />
            <TipChip icon={<I.ShieldCheck size={14} />} title="不用于训练" desc="论文文本不进入模型训练集" />
          </div>

          <p className="mt-8 text-[12px] text-ink-400 leading-relaxed text-center">
            文件检测完成后将在 <span className="text-ink-700 font-medium">24 小时内自动删除</span>，不留底、不外传第三方。
            <br />检测结果仅供作者自查改进使用，不构成最终学术鉴定。
          </p>
        </div>
      </div>
    </div>
  );
}

function DropZone({ dragging, setDragging, onPick }) {
  const fileRef = useRU(null);
  return (
    <div
      onDragOver={(e) => { e.preventDefault(); setDragging(true); }}
      onDragLeave={() => setDragging(false)}
      onDrop={(e) => { e.preventDefault(); setDragging(false); const f = e.dataTransfer.files?.[0]; onPick(f); }}
      onClick={() => fileRef.current?.click()}
      className={`relative cursor-pointer rounded-xl border-2 border-dashed transition-all p-12 text-center
        ${dragging ? 'border-brand-500 bg-brand-50' : 'border-ink-300 hover:border-brand-500 hover:bg-brand-50/40'}`}
    >
      <input ref={fileRef} type="file" accept=".docx,.pdf,.txt" className="hidden" onChange={(e) => onPick(e.target.files?.[0])} />
      <div className={`w-16 h-16 mx-auto rounded-2xl flex items-center justify-center transition-colors
        ${dragging ? 'bg-brand-600 text-white' : 'bg-navy-900 text-white'}`}>
        <I.Upload size={26} />
      </div>
      <div className="mt-5 text-[17px] font-semibold text-navy-900">{dragging ? '松开即可上传' : '拖拽文件至此处 · 或 点击选择文件'}</div>
      <div className="mt-2 text-[13px] text-ink-500">.docx · .pdf · .txt &nbsp;|&nbsp; 单文件 ≤ 10MB</div>
      <div className="mt-6 inline-flex items-center gap-2 text-[12.5px] text-brand-600 font-medium">
        <I.Sparkles size={14} /> 首次免费试用前 1000 字
      </div>
    </div>
  );
}

function UploadingPanel({ name, progress }) {
  return (
    <div>
      <div className="flex items-center gap-3">
        <div className="w-10 h-10 rounded-lg bg-brand-50 text-brand-600 flex items-center justify-center"><I.File size={18} /></div>
        <div className="flex-1 min-w-0">
          <div className="text-[14.5px] font-medium text-navy-900 truncate">{name}</div>
          <div className="text-[12px] text-ink-500 mt-0.5 num">上传中 · <span className="text-brand-600">{Math.round(progress)}%</span></div>
        </div>
        <I.Spinner size={20} className="text-brand-600" />
      </div>
      <div className="mt-4 h-2 rounded-full bg-ink-100 overflow-hidden">
        <div className="h-full bg-brand-600 rounded-full transition-[width] duration-200" style={{ width: `${progress}%` }} />
      </div>
      <div className="mt-4 grid grid-cols-3 gap-2 text-center">
        {['上传文件', '解析文本', '准备检测'].map((s, i) => {
          const done = progress >= [33, 70, 100][i];
          const active = !done && progress >= [0, 33, 70][i];
          return (
            <div key={s} className={`py-2.5 rounded-lg text-[12px] flex items-center justify-center gap-1.5 ring-1 ${done ? 'bg-ok-50 ring-ok-100 text-ok-600' : active ? 'bg-brand-50 ring-brand-100 text-brand-600' : 'bg-ink-50 ring-ink-200 text-ink-400'}`}>
              {done ? <I.Check size={12} /> : active ? <I.Spinner size={12} /> : <I.Dot className="bg-ink-300" />}
              {s}
            </div>
          );
        })}
      </div>
    </div>
  );
}

function ParsingPanel({ name }) {
  const stages = [
    { label: '提取文本内容', detail: '正在从 .docx 中抽取段落 ...', dur: 600 },
    { label: '识别引用与脚注', detail: '排除参考文献部分，避免误判', dur: 500 },
    { label: '初步统计建模', detail: '建立句长 / 句法骨架特征向量', dur: 700 },
  ];
  const [idx, setIdx] = useSU(0);
  useEU(() => {
    if (idx >= stages.length) return;
    const t = setTimeout(() => setIdx(i => i + 1), stages[idx].dur);
    return () => clearTimeout(t);
  }, [idx]);
  return (
    <div className="fade-up">
      <div className="flex items-center gap-3 pb-5 border-b border-ink-200">
        <div className="w-11 h-11 rounded-lg bg-brand-50 text-brand-600 flex items-center justify-center ring-1 ring-brand-100">
          <I.Spinner size={18} />
        </div>
        <div className="flex-1 min-w-0">
          <div className="text-[15px] font-semibold text-navy-900 truncate">{name}</div>
          <div className="text-[12.5px] text-brand-600 mt-0.5">正在解析文档结构 ...</div>
        </div>
        <Badge tone="brand"><span className="w-1.5 h-1.5 rounded-full bg-brand-500 pulse-dot" />检测引擎已就绪</Badge>
      </div>

      <div className="mt-5 relative h-44 rounded-xl bg-ink-50 ring-1 ring-ink-200 overflow-hidden">
        <div className="absolute inset-0 p-5 space-y-2">
          {Array.from({ length: 9 }).map((_, i) => (
            <div key={i} className="h-2 rounded-full bg-ink-200"
                 style={{ width: `${[92, 78, 84, 90, 64, 88, 76, 82, 68][i]}%`, opacity: 0.55 + (i % 3) * 0.12 }} />
          ))}
        </div>
        <div className="absolute left-0 right-0 h-[2px] bg-gradient-to-b from-transparent via-brand-600 to-transparent shadow-[0_0_24px_4px_rgba(37,99,235,0.45)]"
             style={{ animation: 'scanLine 2.2s linear infinite' }} />
        <div className="absolute top-2 right-2 mono text-[10px] text-brand-600">PARSING · {Math.min(99, idx * 33 + 5)}%</div>
      </div>

      <ol className="mt-5 space-y-2">
        {stages.map((s, i) => {
          const done = i < idx;
          const active = i === idx;
          return (
            <li key={s.label} className={`flex items-start gap-3 p-3 rounded-lg ring-1 transition-colors
              ${done ? 'bg-ok-50 ring-ok-100' : active ? 'bg-brand-50 ring-brand-100' : 'bg-ink-50 ring-ink-200'}`}>
              <span className={`mt-0.5 w-5 h-5 rounded-full flex items-center justify-center flex-shrink-0
                ${done ? 'bg-ok-500 text-white' : active ? 'bg-brand-600 text-white' : 'bg-ink-200 text-ink-400'}`}>
                {done ? <I.Check size={11} /> : active ? <I.Spinner size={11} /> : <I.Dot className="bg-white" />}
              </span>
              <div className="flex-1 min-w-0">
                <div className={`text-[13.5px] font-medium ${done || active ? 'text-navy-900' : 'text-ink-400'}`}>{s.label}</div>
                <div className={`text-[11.5px] ${done ? 'text-ok-600' : active ? 'text-brand-600' : 'text-ink-400'}`}>{s.detail}</div>
              </div>
            </li>
          );
        })}
      </ol>
    </div>
  );
}

function PrescanPanel({ meta, onContinue }) {
  const previewParas = [
    { risk: 'high', text: '在新时代的背景下，本文将从多个维度对该问题进行深入剖析，构建系统化的理论分析框架，为后续研究提供有益参考。' },
    { risk: 'med',  text: '研究表明，相关因素对于研究对象具有显著的促进作用。综上所述，本研究的开展具有重要意义。' },
    { risk: 'high', text: '从总体上看，可以将其概括为三个方面，分别体现在认知、情感与行为三个层面，共同发挥着积极作用。' },
  ];
  return (
    <div className="fade-up">
      <div className="flex items-center gap-3 pb-5 border-b border-ink-200">
        <div className="w-11 h-11 rounded-lg bg-navy-900 text-white flex items-center justify-center"><I.FileText size={20} /></div>
        <div className="flex-1 min-w-0">
          <div className="text-[15px] font-semibold text-navy-900 truncate">{meta.name}</div>
          <div className="text-[12.5px] text-ink-500 mt-0.5 flex items-center gap-2">
            <Badge tone="ok"><I.Check size={11} /> 解析完成</Badge>
            <span>·</span>
            <span className="num">{meta.words.toLocaleString()} 字 · {meta.sizeKb} KB</span>
          </div>
        </div>
      </div>

      <div className="mt-5 p-5 rounded-xl bg-bad-50 ring-1 ring-bad-100">
        <div className="flex items-start gap-3">
          <div className="w-9 h-9 rounded-lg bg-white text-bad-600 flex items-center justify-center ring-1 ring-bad-100 flex-shrink-0"><I.AlertTriangle size={18} /></div>
          <div className="flex-1">
            <div className="text-[13px] font-semibold text-bad-600 tracking-wide">预扫描提示 · 初步识别</div>
            <div className="mt-1 text-[15px] font-semibold text-navy-900">已识别到 <span className="num text-bad-600">3 段</span> 疑似 AI 生成痕迹</div>
            <div className="mt-1 text-[12.5px] text-ink-500">付费后查看完整标记、模型指纹匹配与改写建议。预扫描仅基于句法模板初判，正式报告将启用全部 5 维度统计模型。</div>
          </div>
        </div>

        <div className="mt-4 space-y-2">
          {previewParas.map((p, i) => {
            const tone = p.risk === 'high' ? { bg: '#FEF2F2', bar: '#EF4444', text: 'text-bad-600' } : { bg: '#FFFBEB', bar: '#F59E0B', text: 'text-warn-600' };
            return (
              <div key={i} className="relative pl-3 pr-3 py-2.5 rounded-md ring-1 ring-ink-200" style={{ background: tone.bg }}>
                <span className="absolute top-0 bottom-0 left-0 w-1 rounded-l-md" style={{ background: tone.bar }} />
                <div className="text-[12px] text-ink-700 leading-relaxed pl-2">
                  <span className={`mono text-[10.5px] mr-1.5 ${tone.text}`}>§ {i + 1}</span>
                  {p.text}
                </div>
              </div>
            );
          })}
          <div className="text-center text-[11.5px] text-ink-400 mt-2">… 还有 <span className="num font-semibold">25 处</span> 段落分析未展示 · 解锁完整报告即可查看</div>
        </div>
      </div>

      <div className="mt-5 p-4 rounded-xl bg-brand-50 ring-1 ring-brand-100 flex items-start gap-3">
        <I.Sparkles size={16} className="text-brand-600 mt-0.5" />
        <div className="flex-1 text-[12.5px] text-brand-600 leading-relaxed">
          检测引擎已就绪 · 本次检测将与 <strong>8 大主流模型指纹</strong>比对 · <strong>30 秒</strong>内生成完整报告
        </div>
      </div>

      <div className="mt-6 flex flex-col-reverse sm:flex-row items-stretch sm:items-center gap-3">
        <Button kind="secondary" size="lg" onClick={onContinue} className="sm:flex-shrink-0">查看推荐方案</Button>
        <Button kind="primary" size="lg" onClick={onContinue} iconRight={<I.ArrowRight size={16} />} className="flex-1">
          ¥{meta.amount.toFixed(2)} 解锁完整报告
        </Button>
      </div>
    </div>
  );
}

function FileInfoCard({ meta, onReplace, onPay }) {
  const [wordsAnim, setWordsAnim] = useSU(0);
  useEU(() => {
    let raf, start;
    const dur = 1400;
    const step = (ts) => {
      if (!start) start = ts;
      const t = Math.min(1, (ts - start) / dur);
      const e = 1 - Math.pow(1 - t, 3);
      setWordsAnim(Math.round(meta.words * e));
      if (t < 1) raf = requestAnimationFrame(step);
    };
    raf = requestAnimationFrame(step);
    return () => cancelAnimationFrame(raf);
  }, [meta.words]);

  return (
    <div className="fade-up">
      <div className="flex items-center gap-3 pb-5 border-b border-ink-200">
        <div className="w-11 h-11 rounded-lg bg-navy-900 text-white flex items-center justify-center"><I.FileText size={20} /></div>
        <div className="flex-1 min-w-0">
          <div className="text-[15px] font-semibold text-navy-900 truncate">{meta.name}</div>
          <div className="text-[12.5px] text-ink-500 mt-0.5 flex items-center gap-2">
            <Badge tone="ok"><I.Check size={11} /> 上传成功</Badge>
            <span>·</span>
            <span>已通过完整性校验</span>
          </div>
        </div>
      </div>

      <dl className="mt-5 grid grid-cols-2 gap-x-6 gap-y-4">
        <Row label="文件大小" value={`${meta.sizeKb} KB`} />
        <Row label="解析字数" value={<span className="num font-semibold text-navy-900 text-[18px]">{wordsAnim.toLocaleString()}</span>} suffix="字" />
        <Row label="订单号" value={<span className="mono text-[12px]">{meta.orderId}</span>} />
        <Row label="应付金额" value={<span className="num font-semibold text-bad-600 text-[18px]">¥{meta.amount.toFixed(2)}</span>} />
      </dl>

      <div className="mt-6 p-4 rounded-xl bg-brand-50 ring-1 ring-brand-100 flex items-start gap-3">
        <div className="w-9 h-9 rounded-lg bg-white text-brand-600 flex items-center justify-center ring-1 ring-brand-100"><I.Target size={16} /></div>
        <div className="flex-1">
          <div className="text-[12.5px] tracking-wide font-semibold text-brand-600">推荐方案 · 基于解析字数</div>
          <div className="mt-1 text-[15px] font-semibold text-navy-900">{meta.plan}</div>
          <div className="mt-0.5 text-[12.5px] text-ink-500">您的论文为 {meta.words.toLocaleString()} 字，已自动匹配最合适的方案。</div>
        </div>
      </div>

      <div className="mt-6 flex flex-col-reverse sm:flex-row items-stretch sm:items-center gap-3">
        <Button kind="secondary" size="lg" onClick={onReplace} icon={<I.Refresh size={14} />} className="sm:flex-shrink-0">重新上传</Button>
        <Button kind="primary" size="lg" onClick={onPay} iconRight={<I.ArrowRight size={16} />} className="flex-1">进入支付</Button>
      </div>
    </div>
  );
}

function ErrorPanel({ error, onRetry }) {
  const msg = error?.message || '上传失败，请重试';
  return (
    <div className="fade-up text-center py-6">
      <div className="w-14 h-14 mx-auto rounded-full bg-bad-50 ring-1 ring-bad-100 text-bad-600 flex items-center justify-center">
        <I.AlertTriangle size={26} />
      </div>
      <h3 className="mt-4 text-[20px] font-bold text-navy-900 tracking-tight">上传失败</h3>
      <p className="mt-2 text-[13.5px] text-ink-500 leading-relaxed max-w-md mx-auto">{msg}</p>
      {error?.code && <p className="mt-1 text-[11px] text-ink-400 mono">code: {error.code}</p>}
      <Button kind="dark" size="lg" onClick={onRetry} icon={<I.Refresh size={14} />} className="mt-6">重新上传</Button>
    </div>
  );
}

function Row({ label, value, suffix }) {
  return (
    <div>
      <dt className="text-[12px] text-ink-500 tracking-wide">{label}</dt>
      <dd className="mt-0.5 text-[15px] text-navy-900 font-medium num">{value}{suffix && <span className="text-ink-500 ml-1 text-[13px]">{suffix}</span>}</dd>
    </div>
  );
}

function TipChip({ icon, title, desc }) {
  return (
    <div className="flex items-start gap-2.5 p-3 rounded-lg bg-white ring-1 ring-ink-200">
      <div className="w-7 h-7 rounded-md bg-ink-100 text-navy-900 flex items-center justify-center flex-shrink-0">{icon}</div>
      <div>
        <div className="text-[13px] font-semibold text-navy-900">{title}</div>
        <div className="text-[11.5px] text-ink-500 mt-0.5">{desc}</div>
      </div>
    </div>
  );
}

window.UploadPage = UploadPage;
