// 5 · 数据脱敏与扰乱 (/scrub)
//   - 左：脱敏方案表单（相机 + 相机参数 + 饱和度/颗粒度/版权/作者/软件/日期）+ 说明面板
//   - 中：命中 AIGC 元数据的 clip 列表 + 单条 / 批量脱敏
//   - 顶部：底层依赖健康灯 + 扫描分镜库 + 一键脱敏命中项

// 一行表单：左侧 label，右侧任意 control
const ProfileFieldRow = ({ label, children }) => (
  <div className="row" style={{ gap: 8, alignItems: "center" }}>
    <span style={{ fontSize: 11, color: "var(--fg-2)", width: 64, flexShrink: 0 }}>{label}</span>
    <div style={{ flex: 1 }}>{children}</div>
  </div>
);

// 内联样式给本页 select/input 共用（避免污染全局 styles.css）
const _ppInputStyle = {
  width: "100%", padding: "5px 8px", fontSize: 11.5,
  border: "1px solid var(--line-1)", borderRadius: 4,
  background: "white", color: "var(--fg-0)", fontFamily: "inherit",
};
// 注：JSX 不支持 className → style 注入，于是我用一个轻量 CSS-in-JS 钩子：
// 第一次组件挂载时给页面注入一段 .ppinput 规则
(function injectPPInputCSS() {
  if (typeof document === "undefined") return;
  if (document.getElementById("ppinput-css")) return;
  const s = document.createElement("style");
  s.id = "ppinput-css";
  s.textContent = `
    .ppinput { width: 100%; padding: 5px 8px; font-size: 11.5px;
               border: 1px solid var(--line-1); border-radius: 4px;
               background: white; color: var(--fg-0); font-family: inherit; }
    .ppinput:disabled { background: var(--bg-1); color: var(--fg-3); cursor: not-allowed; }
  `;
  document.head.appendChild(s);
})();


// 默认 profile（沿用后端 ScrubProfile 默认值）
const DEFAULT_PROFILE = {
  camera_id: "off",
  camera_param_id: "",
  saturation: 1.0,
  grain_strength: 3.0,
  copyright: "",
  artist: "",
  software: "光年AI 后处理 v1",
  description: "",
  capture_date: "",
};

const ScrubPage = () => {
  const [health, setHealth]       = React.useState(null);
  const [scanData, setScanData]   = React.useState(null);
  const [scanning, setScanning]   = React.useState(false);
  const [scrubbing, setScrubbing] = React.useState(false);
  const [busyId, setBusyId]       = React.useState("");
  const [err, setErr]             = React.useState("");
  const [lastScrub, setLastScrub] = React.useState(null);
  const [catalog, setCatalog]     = React.useState(null);   // {cameras, saturation, grain, software}
  const [profile, setProfile]     = React.useState(DEFAULT_PROFILE);
  // 顶部库切换 + 图片库扫描
  const [mode, setMode]                       = React.useState("shots"); // "shots" | "images"
  const [scanImagesData, setScanImagesData]   = React.useState(null);
  const [scanningImages, setScanningImages]   = React.useState(false);

  React.useEffect(() => { (async () => {
    try {
      const [h, c] = await Promise.all([
        API.postprocess.health(),
        API.postprocess.cameras(),
      ]);
      setHealth(h);
      setCatalog(c);
    }
    catch (e) { setErr(String(e.message || e)); }
  })(); }, []);

  // 切换相机时清空相机参数（避免上一相机的 preset id 残留）
  const onCameraChange = (camId) => {
    setProfile(p => ({ ...p, camera_id: camId, camera_param_id: "" }));
  };
  const setProfileField = (k, v) => setProfile(p => ({ ...p, [k]: v }));

  const doScan = async () => {
    if (scanning) return;
    setScanning(true); setErr("");
    try { setScanData(await API.postprocess.scanLibrary()); }
    catch (e) { setErr(`扫描失败：${e.message || e}`); }
    finally { setScanning(false); }
  };

  const doScanImages = async () => {
    if (scanningImages) return;
    setScanningImages(true); setErr("");
    setMode("images");
    try { setScanImagesData(await API.postprocess.scanImageLibrary()); }
    catch (e) { setErr(`扫描图片失败：${e.message || e}`); }
    finally { setScanningImages(false); }
  };

  const doScrubAll = async () => {
    if (scrubbing) return;
    if (!confirm("将对所有命中 AIGC 元数据的 clip 就地脱敏（不可恢复）。\n继续？")) return;
    setScrubbing(true); setErr("");
    try {
      const r = await API.postprocess.scrubLibrary(profile);
      setLastScrub(r);
      await doScan();
    } catch (e) { setErr(`批量脱敏失败：${e.message || e}`); }
    finally { setScrubbing(false); }
  };

  const doScrubOne = async (clipId) => {
    if (busyId) return;
    setBusyId(clipId); setErr("");
    try {
      await API.postprocess.scrubClip(clipId, profile);
      await doScan();
    } catch (e) { setErr(`脱敏失败：${e.message || e}`); }
    finally { setBusyId(""); }
  };

  // 当前相机的 params 子数组（依赖于 camera_id —— 上下级关系）
  const currentCamera = (catalog?.cameras || []).find(c => c.id === profile.camera_id);
  const cameraParams = currentCamera?.params || [];

  const items = (scanData?.items || []).sort((a, b) => {
    const order = { high: 0, low: 1, none: 2 };
    return (order[a.confidence] ?? 3) - (order[b.confidence] ?? 3);
  });
  const aigcCount = scanData?.aigc || 0;
  const total = scanData?.total || 0;

  return (
    <>
      <Topbar
        crumbs={["工作台", "5 · 数据脱敏与扰乱"]}
        right={<>
          <span className="pill" style={{
            background: health?.status === "ok"
              ? "color-mix(in oklch, var(--green) 14%, var(--bg-1))"
              : "color-mix(in oklch, var(--pink) 14%, var(--bg-1))",
            color: health?.status === "ok" ? "var(--green)" : "var(--pink)",
          }}>
            <Icon name="zap" size={10}/>
            底层依赖 {health?.status === "ok" ? "正常" : "缺失"}
          </span>
          <button className="btn ghost" onClick={doScan} disabled={scanning}>
            <Icon name="refresh" size={11}/>{scanning ? "扫描中…" : "扫描分镜库"}
          </button>
          <button className="btn ghost" onClick={doScanImages} disabled={scanningImages}>
            <Icon name="grid" size={11}/>{scanningImages ? "扫描中…" : "扫描图片"}
          </button>
          <button className="btn primary" onClick={doScrubAll}
                  disabled={scrubbing || !aigcCount}
                  style={{ background: "var(--purple)", color: "var(--bg-0)" }}>
            <Icon name="play" size={11}/>
            {scrubbing ? "脱敏中…" : `一键脱敏命中项 (${aigcCount})`}
          </button>
        </>}
      />

      {err && (
        <div style={{ margin: "10px 16px 0", padding: "8px 12px",
                      background: "color-mix(in oklch, var(--pink) 14%, var(--bg-1))",
                      color: "var(--pink)", borderRadius: 4, fontSize: 11 }}>{err}</div>
      )}

      <div className="main-body">
        {/* 左：脱敏方案表单 + 说明面板 */}
        <div className="col scroll" style={{
          width: 360, flexShrink: 0, padding: "14px 14px 24px",
          gap: 12, borderRight: "1px solid var(--line-soft)",
        }}>
          {/* ───── 脱敏方案（profile） ───── */}
          <div className="card" style={{ padding: 0 }}>
            <div className="card-hd" style={{ padding: "8px 12px" }}>
              <span className="card-title">脱敏方案</span>
              <span className="card-sub">每次脱敏都会按此方案写入元数据</span>
            </div>
            <div className="col" style={{ padding: 12, gap: 8 }}>
              {/* 1. 相机 → 相机参数（上下级） */}
              <ProfileFieldRow label="相机">
                <select className="ppinput" value={profile.camera_id}
                        onChange={e => onCameraChange(e.target.value)}>
                  {(catalog?.cameras || []).map(c => (
                    <option key={c.id} value={c.id}>{c.label}</option>
                  ))}
                </select>
              </ProfileFieldRow>
              <ProfileFieldRow label="相机参数">
                <select className="ppinput" value={profile.camera_param_id}
                        onChange={e => setProfileField("camera_param_id", e.target.value)}
                        disabled={cameraParams.length === 0}>
                  <option value="">{cameraParams.length ? "— 选一个 preset —" : "（先选相机）"}</option>
                  {cameraParams.map(p => (
                    <option key={p.id} value={p.id}>
                      {p.label || p.id}
                    </option>
                  ))}
                </select>
              </ProfileFieldRow>

              {/* 2. 饱和度 / 颗粒度 */}
              <ProfileFieldRow label="饱和度">
                <select className="ppinput" value={String(profile.saturation)}
                        onChange={e => setProfileField("saturation", parseFloat(e.target.value))}>
                  {(catalog?.saturation || []).map(o => (
                    <option key={o.id} value={o.id}>{o.label}</option>
                  ))}
                </select>
              </ProfileFieldRow>
              <ProfileFieldRow label="颗粒度">
                <select className="ppinput" value={String(profile.grain_strength)}
                        onChange={e => setProfileField("grain_strength", parseFloat(e.target.value))}>
                  {(catalog?.grain || []).map(o => (
                    <option key={o.id} value={o.id}>{o.label}</option>
                  ))}
                </select>
              </ProfileFieldRow>

              {/* 3. 版权 / 作者 / 软件 / 日期 */}
              <ProfileFieldRow label="版权">
                <input className="ppinput" placeholder="© 2026 ACME Inc."
                       value={profile.copyright}
                       onChange={e => setProfileField("copyright", e.target.value)}/>
              </ProfileFieldRow>
              <ProfileFieldRow label="作者">
                <input className="ppinput" placeholder="如：ACME Design Team"
                       value={profile.artist}
                       onChange={e => setProfileField("artist", e.target.value)}/>
              </ProfileFieldRow>
              <ProfileFieldRow label="软件">
                <select className="ppinput" value={profile.software}
                        onChange={e => setProfileField("software", e.target.value)}>
                  {(catalog?.software || []).map(s => (
                    <option key={s} value={s}>{s}</option>
                  ))}
                </select>
              </ProfileFieldRow>
              <ProfileFieldRow label="日期">
                <input className="ppinput" type="datetime-local"
                       value={profile.capture_date.slice(0, 16)}
                       onChange={e => setProfileField("capture_date", e.target.value)}/>
              </ProfileFieldRow>

              <div className="row between" style={{ marginTop: 4 }}>
                <button className="btn sm ghost" onClick={() => setProfile(DEFAULT_PROFILE)}
                        style={{ fontSize: 10.5 }}>重置默认</button>
                <span style={{ fontSize: 9.5, color: "var(--fg-3)" }}>方案不持久化，刷新即丢</span>
              </div>
            </div>
          </div>

          <div className="card" style={{ padding: 12 }}>
            <div className="card-title" style={{ marginBottom: 6 }}>这个组件做什么</div>
            <ul style={{ fontSize: 11.5, lineHeight: 1.65, color: "var(--fg-1)",
                         margin: 0, paddingLeft: 16 }}>
              <li>剥离原 EXIF / 容器元数据（Software / Comment / creation_time / PNG parameters …）</li>
              <li>图：重像素化 + 颗粒 + USM + 微旋 + WebP↔JPEG 多次有损洗荡</li>
              <li>视：ffmpeg 重编码 + 可选颗粒</li>
              <li>写入<b>真实</b>元数据（版权 / 作者 / 软件 / 日期）—— 不伪造相机型号 / GPS</li>
            </ul>
          </div>

          <div className="card" style={{ padding: 12 }}>
            <div className="card-title" style={{ marginBottom: 6 }}>自动触发点</div>
            <ul style={{ fontSize: 11.5, lineHeight: 1.65, color: "var(--fg-1)",
                         margin: 0, paddingLeft: 16 }}>
              <li><b>复刻爆款</b>：云端视频每个镜头下载完，自动经过</li>
              <li><b>分镜素材库</b>：clip 入库 ingest 时，自动检测 AIGC 元数据，命中即脱敏</li>
              <li>这两个入口外，可在本页手动「扫描 + 脱敏」</li>
            </ul>
          </div>

          <div className="card" style={{ padding: 12 }}>
            <div className="card-title" style={{ marginBottom: 6 }}>底层依赖</div>
            <div className="col gap-4" style={{ fontSize: 11 }}>
              {Object.entries(health?.deps || {}).map(([k, v]) => (
                <div key={k} className="row between">
                  <span className="mono" style={{ color: "var(--fg-2)" }}>{k}</span>
                  <span style={{ color: String(v).startsWith("ok") || String(v).match(/^\d/) ? "var(--green)" : "var(--pink)" }}>
                    {String(v)}
                  </span>
                </div>
              ))}
            </div>
          </div>

          {lastScrub && (
            <div className="card" style={{ padding: 12 }}>
              <div className="card-title" style={{ marginBottom: 6 }}>上次批量脱敏</div>
              <div style={{ fontSize: 11, color: "var(--fg-2)" }}>
                成功 {lastScrub.cleaned} · 失败 {lastScrub.failed} · 跳过 {lastScrub.skipped}
              </div>
            </div>
          )}
        </div>

        {/* 中：扫描结果 */}
        <div className="col grow scroll" style={{
          padding: "14px 14px 24px", background: "var(--bg-0)",
        }}>
          {/* 顶部：库切换 */}
          <div className="row" style={{ gap: 6, marginBottom: 12 }}>
            {[
              { id: "shots",  label: "分镜库",  count: scanData?.total },
              { id: "images", label: "图片库",  count: scanImagesData?.total },
            ].map(t => {
              const active = mode === t.id;
              return (
                <button
                  key={t.id}
                  className="btn sm"
                  onClick={() => setMode(t.id)}
                  style={{
                    background: active ? "var(--fg-0)" : "transparent",
                    color: active ? "var(--bg-0)" : "var(--fg-1)",
                    border: active ? "none" : "1px solid var(--line-1)",
                    fontSize: 11.5, padding: "4px 12px",
                  }}>
                  {t.label}{typeof t.count === "number" ? `（${t.count}）` : ""}
                </button>
              );
            })}
          </div>

          {mode === "images" ? (
            !scanImagesData ? (
              <div className="card" style={{
                padding: "60px 20px", textAlign: "center",
                color: "var(--fg-3)", fontSize: 13,
              }}>
                点击右上「扫描图片」开始检测图片库…
              </div>
            ) : (scanImagesData.items || []).length === 0 ? (
              <div className="card" style={{
                padding: "60px 20px", textAlign: "center",
                color: "var(--fg-2)", fontSize: 13,
              }}>
                <div style={{ fontSize: 22, marginBottom: 8 }}>✓</div>
                图片库 {scanImagesData.total || 0} 张全部干净，无 AIGC 元数据信号
              </div>
            ) : (
              <div className="card" style={{ padding: 0 }}>
                <div className="card-hd row between" style={{ padding: "10px 12px" }}>
                  <span className="card-title">图片库扫描结果</span>
                  <span className="kpi-l">
                    {scanImagesData.aigc || 0} 命中 / {scanImagesData.total || 0} 总数
                  </span>
                </div>
                {(scanImagesData.items || []).map((it, i) => (
                  <div key={it.image_id || it.id || i} className="row" style={{
                    padding: "9px 12px",
                    borderTop: i > 0 ? "1px solid var(--line-1)" : "none",
                    gap: 10, alignItems: "center",
                  }}>
                    <span className={`tag ${it.confidence === "high" ? "pink" : it.confidence === "low" ? "" : "green"}`} style={{ fontSize: 9.5 }}>
                      {it.confidence === "high" ? "高置信" : it.confidence === "low" ? "弱信号" : "干净"}
                    </span>
                    <span style={{ fontSize: 12, fontWeight: 500, color: "var(--fg-0)",
                                   maxWidth: 340, overflow: "hidden",
                                   textOverflow: "ellipsis", whiteSpace: "nowrap" }}
                          title={it.name}>
                      {it.name}
                    </span>
                    {it.is_aigc && Array.isArray(it.signals) && (
                      <span className="mono" style={{ fontSize: 10, color: "var(--fg-3)" }}>
                        {it.signals.slice(0, 2).map(s => `${s.source}: ${s.marker}`).join(" · ")}
                        {it.signals.length > 2 ? ` +${it.signals.length - 2}` : ""}
                      </span>
                    )}
                  </div>
                ))}
              </div>
            )
          ) : !scanData ? (
            <div className="card" style={{
              padding: "60px 20px", textAlign: "center",
              color: "var(--fg-3)", fontSize: 13,
            }}>
              点击右上「扫描分镜库」开始检测…
            </div>
          ) : items.length === 0 ? (
            <div className="card" style={{
              padding: "60px 20px", textAlign: "center",
              color: "var(--fg-2)", fontSize: 13,
            }}>
              <div style={{ fontSize: 22, marginBottom: 8 }}>✓</div>
              分镜库 {total} 条 clip 全部干净，无 AIGC 元数据信号
            </div>
          ) : (
            <div className="card" style={{ padding: 0 }}>
              <div className="card-hd row between" style={{ padding: "10px 12px" }}>
                <span className="card-title">扫描结果</span>
                <span className="kpi-l">
                  {aigcCount} 命中 / {total} 总数
                </span>
              </div>
              {items.map((it, i) => (
                <div key={it.clip_id} className="row" style={{
                  padding: "9px 12px",
                  borderTop: i > 0 ? "1px solid var(--line-1)" : "none",
                  gap: 10, alignItems: "center",
                }}>
                  <span className={`tag ${it.confidence === "high" ? "pink" : it.confidence === "low" ? "" : "green"}`} style={{ fontSize: 9.5 }}>
                    {it.confidence === "high" ? "高置信" : it.confidence === "low" ? "弱信号" : "干净"}
                  </span>
                  <span style={{ fontSize: 12, fontWeight: 500, color: "var(--fg-0)",
                                 maxWidth: 340, overflow: "hidden",
                                 textOverflow: "ellipsis", whiteSpace: "nowrap" }}
                        title={it.name}>
                    {it.name}
                  </span>
                  {it.is_aigc && (
                    <span className="mono" style={{ fontSize: 10, color: "var(--fg-3)" }}>
                      {it.signals.slice(0, 2).map(s => `${s.source}: ${s.marker}`).join(" · ")}
                      {it.signals.length > 2 ? ` +${it.signals.length - 2}` : ""}
                    </span>
                  )}
                  <span style={{ flex: 1 }}/>
                  {it.is_aigc && (
                    <button
                      className="btn sm primary"
                      onClick={() => doScrubOne(it.clip_id)}
                      disabled={busyId === it.clip_id}
                      style={{ background: "var(--purple)", color: "var(--bg-0)", fontSize: 10.5 }}
                    >
                      {busyId === it.clip_id ? "脱敏中…" : "脱敏"}
                    </button>
                  )}
                </div>
              ))}
            </div>
          )}
        </div>
      </div>
    </>
  );
};

Object.assign(window, { ScrubPage });
