/* ============================================================
   谁是天眼 · 共享 UI 组件 → window
   ============================================================ */
const { useState, useEffect, useRef } = React;

/* ---------- 图标 ---------- */
const Icon = {
  eye: (p = {}) => (
    <svg width={p.s || 24} height={p.s || 24} viewBox="0 0 48 48" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" {...p}>
      <path d="M3 24 C12 11 36 11 45 24 C36 37 12 37 3 24 Z" />
      <circle cx="24" cy="24" r="8" />
      <circle cx="24" cy="24" r="3" fill="currentColor" stroke="none" />
      <path d="M24 6 v3 M24 39 v3 M9 12 l2 2 M37 34 l2 2 M39 12 l-2 2 M11 34 l-2 2" opacity="0.7" />
    </svg>
  ),
  search: (p = {}) => (
    <svg width={p.s || 20} height={p.s || 20} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <circle cx="11" cy="11" r="7" /><path d="m21 21-4.3-4.3" />
    </svg>
  ),
  plus: (p = {}) => (
    <svg width={p.s || 20} height={p.s || 20} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M12 5v14M5 12h14" /></svg>
  ),
  arrow: (p = {}) => (
    <svg width={p.s || 16} height={p.s || 16} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14M13 6l6 6-6 6" /></svg>
  ),
  shield: (p = {}) => (
    <svg width={p.s || 22} height={p.s || 22} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M12 3 4 6v6c0 5 3.5 8 8 9 4.5-1 8-4 8-9V6z" /><path d="m9 12 2 2 4-4" /></svg>
  ),
  warn: (p = {}) => (
    <svg width={p.s || 18} height={p.s || 18} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M12 9v4M12 17h.01" /><path d="M10.3 3.9 2 18a2 2 0 0 0 1.7 3h16.6a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z" /></svg>
  ),
  lock: (p = {}) => (
    <svg width={p.s || 18} height={p.s || 18} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><rect x="4" y="11" width="16" height="9" rx="2" /><path d="M8 11V7a4 4 0 0 1 8 0v4" /></svg>
  ),
  file: (p = {}) => (
    <svg width={p.s || 18} height={p.s || 18} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z" /><path d="M14 3v6h6" /></svg>
  ),
  bell: (p = {}) => (
    <svg width={p.s || 18} height={p.s || 18} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" /><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0" /></svg>
  ),
  gavel: (p = {}) => (
    <svg width={p.s || 18} height={p.s || 18} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="m14 13-7.5 7.5a2.1 2.1 0 0 1-3-3L11 10" /><path d="m16 16 6-6M8 8l6-6M9 7l8 8M21 11l-8-8" /></svg>
  ),
  trash: (p = {}) => (
    <svg width={p.s || 14} height={p.s || 14} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M3 6h18M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2m2 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /></svg>
  ),
  check: (p = {}) => (
    <svg width={p.s || 14} height={p.s || 14} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M20 6 9 17l-5-5" /></svg>
  ),
  x: (p = {}) => (
    <svg width={p.s || 14} height={p.s || 14} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round"><path d="M18 6 6 18M6 6l12 12" /></svg>
  ),
  camera: (p = {}) => (
    <svg width={p.s || 18} height={p.s || 18} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z" /><circle cx="12" cy="13" r="4" /></svg>
  ),
  pin: (p = {}) => (
    <svg width={p.s || 14} height={p.s || 14} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z" /><circle cx="12" cy="10" r="3" /></svg>
  )
};

/* ---------- 天眼标识 ---------- */
function Brand({ onClick, big }) {
  return (
    <div className="brand" onClick={onClick}>
      <span className="mark"><img src="assets/logo.png" alt="" /></span>
      <span className="name">谁是天眼<span className="sub">Tianyan Doc</span></span>
    </div>
  );
}

/* ---------- 日期格式 ---------- */
function fmtDate(ts) {
  const d = new Date(ts);
  return d.getFullYear() + "." + String(d.getMonth() + 1).padStart(2, "0") + "." + String(d.getDate()).padStart(2, "0");
}
function fmtAgo(ts) {
  const diff = Date.now() - ts;
  const h = Math.floor(diff / 3600000);
  if (h < 1) return "刚刚";
  if (h < 24) return h + " 小时前";
  return Math.floor(h / 24) + " 天前";
}

/* ---------- 状态徽标 ---------- */
function StatusPill({ status }) {
  const map = {
    active: ["pill-red", "已记录 · 生效中"],
    appealing: ["pill-warn", "申诉处理中"],
    cleared: ["pill-safe", "申诉通过 · 已撤档"]
  };
  const [cls, label] = map[status] || map.active;
  return <span className={"pill " + cls}>{label}</span>;
}

/* ---------- 档案卡 (dossier) ---------- */
function Dossier({ rec, onAppeal, redact }) {
  const stampLabel = rec.status === "cleared" ? "已撤档" : rec.status === "appealing" ? "申诉中" : "已记录";
  const stampCls = rec.status === "cleared" ? "cleared" : rec.status === "appealing" ? "appealing" : "";
  // redact: 是否对部分敏感信息打码（查询结果可选）
  const mask = (s) => {
    if (!redact || !s) return s;
    if (s.length <= 2) return s[0] + "*";
    return s.slice(0, 2) + "*".repeat(Math.max(1, s.length - 3)) + s.slice(-1);
  };
  return (
    <div className="dossier">
      <div className={"stamp " + stampCls}>{stampLabel}</div>
      <div className="dossier-head">
        <div style={{ flex: 1 }}>
          <div className="case-no">档案编号 {rec.id}</div>
          <div className="name">{rec.nickname}</div>
        </div>
      </div>
      <div className="dossier-body">
        <div className="rec-grid">
          <div className="rec-cell"><div className="k">涉及剧本</div><div className="v">{rec.script}</div></div>
          <div className="rec-cell"><div className="k">发生店铺</div><div className="v">{rec.shop}</div></div>
          {rec.region && <div className="rec-cell"><div className="k">城市</div><div className="v">{rec.region}</div></div>}
          <div className="rec-cell"><div className="k">微信号</div><div className="v code">{rec.wechat ? mask(rec.wechat) : "—"}</div></div>
          <div className="rec-cell">
            <div className="k">特征信息</div>
            <div className="v">
              {rec.features.length === 0 ? "—" : (
                <div className="feature-tags">
                  {rec.features.map((f, i) => (
                    <span className="feature-tag" key={i}>
                      <span className="ft-type">{f.type}</span>
                      <span className="ft-val">{mask(f.value)}</span>
                    </span>
                  ))}
                </div>
              )}
            </div>
          </div>
        </div>
        {rec.note && (
          <div style={{ marginTop: 14, fontSize: 14, lineHeight: 1.65, color: "#433a26" }}>
            <span style={{ fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.16em", color: "#877039", display: "block", marginBottom: 5, textTransform: "uppercase" }}>情况说明</span>
            {rec.note}
          </div>
        )}
        {rec.evidence && rec.evidence.length > 0 && (
          <div style={{ marginTop: 16 }}>
            <span style={{ fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.16em", color: "#877039", display: "block", marginBottom: 8, textTransform: "uppercase" }}>证据照片 · {rec.evidence.length}</span>
            <div className="evidence-grid dossier-evidence">
              {rec.evidence.map((src, i) => (
                <div className="evidence-thumb" key={i}><img src={src} alt="证据" onClick={() => openLightbox(src)} /></div>
              ))}
            </div>
          </div>
        )}
      </div>
      <div className="dossier-foot">
        <span className="meta">登记 {fmtDate(rec.createdAt)} · 来源 {rec.registrant}</span>
        {rec.appeals.length > 0 && <span className="meta">· 已有 {rec.appeals.length} 条申诉</span>}
        <div style={{ marginLeft: "auto" }}>
          {rec.status === "cleared"
            ? <span className="pill pill-safe">已撤档</span>
            : <button className="btn btn-danger" style={{ padding: "9px 16px", fontSize: 13.5 }} onClick={() => onAppeal(rec)}>{Icon.gavel({ s: 15 })} 我要申诉</button>}
        </div>
      </div>
    </div>
  );
}

/* ---------- Toast ---------- */
const ToastCtx = React.createContext(() => {});
function ToastProvider({ children }) {
  const [toasts, setToasts] = useState([]);
  const push = (msg, kind = "ok") => {
    const id = Date.now() + Math.random();
    setToasts(t => [...t, { id, msg, kind }]);
    setTimeout(() => setToasts(t => t.filter(x => x.id !== id)), 3200);
  };
  return (
    <ToastCtx.Provider value={push}>
      {children}
      <div className="toast-wrap">
        {toasts.map(t => (
          <div className={"toast " + t.kind} key={t.id}>
            {t.kind === "ok" ? Icon.check({ s: 16 }) : t.kind === "err" ? Icon.warn({ s: 16 }) : Icon.bell({ s: 16 })}
            <span>{t.msg}</span>
          </div>
        ))}
      </div>
    </ToastCtx.Provider>
  );
}
function useToast() { return React.useContext(ToastCtx); }

/* ---------- 模态 ---------- */
function Modal({ title, sub, onClose, children, foot }) {
  useEffect(() => {
    const h = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", h);
    return () => window.removeEventListener("keydown", h);
  }, []);
  return (
    <div className="modal-overlay" onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="modal" onMouseDown={e => e.stopPropagation()}>
        <div className="modal-head">
          <div>
            <h3>{title}</h3>
            {sub && <div className="sub">{sub}</div>}
          </div>
          <button className="modal-x" onClick={onClose}>×</button>
        </div>
        <div className="modal-body">{children}</div>
        {foot && <div className="modal-foot">{foot}</div>}
      </div>
    </div>
  );
}

/* ---------- 图片：压缩为 dataURL（控制上传体积）---------- */
function fileToDataUrl(file, maxW = 900, quality = 0.72) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      const img = new Image();
      img.onload = () => {
        const scale = Math.min(1, maxW / img.width);
        const w = Math.round(img.width * scale);
        const h = Math.round(img.height * scale);
        const c = document.createElement("canvas");
        c.width = w; c.height = h;
        c.getContext("2d").drawImage(img, 0, 0, w, h);
        resolve(c.toDataURL("image/jpeg", quality));
      };
      img.onerror = reject;
      img.src = reader.result;
    };
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
}

/* ---------- 灯箱（点击放大证据图）---------- */
function openLightbox(src) { window.dispatchEvent(new CustomEvent("open-lightbox", { detail: src })); }
function Lightbox() {
  const [src, setSrc] = useState(null);
  useEffect(() => {
    const h = (e) => setSrc(e.detail);
    const k = (e) => { if (e.key === "Escape") setSrc(null); };
    window.addEventListener("open-lightbox", h);
    window.addEventListener("keydown", k);
    return () => { window.removeEventListener("open-lightbox", h); window.removeEventListener("keydown", k); };
  }, []);
  if (!src) return null;
  return (
    <div className="modal-overlay" style={{ zIndex: 300 }} onClick={() => setSrc(null)}>
      <img src={src} alt="证据" style={{ maxWidth: "92vw", maxHeight: "90vh", borderRadius: 8, boxShadow: "0 30px 80px -20px rgba(0,0,0,.9)" }} />
    </div>
  );
}

/* ---------- 证据上传 ---------- */
function EvidenceUploader({ value, onChange, max = 4 }) {
  const inputRef = useRef(null);
  const [busy, setBusy] = useState(false);
  const pick = async (e) => {
    const files = Array.from(e.target.files || []);
    if (!files.length) return;
    setBusy(true);
    const room = max - value.length;
    const out = [];
    for (const f of files.slice(0, room)) {
      if (!f.type.startsWith("image/")) continue;
      try { out.push(await fileToDataUrl(f)); } catch (err) {}
    }
    onChange([...value, ...out]);
    setBusy(false);
    if (inputRef.current) inputRef.current.value = "";
  };
  return (
    <div>
      <div className="evidence-grid">
        {value.map((src, i) => (
          <div className="evidence-thumb" key={i}>
            <img src={src} alt="证据" onClick={() => openLightbox(src)} />
            <button type="button" className="ev-rm" onClick={() => onChange(value.filter((_, idx) => idx !== i))}>{Icon.x({ s: 12 })}</button>
          </div>
        ))}
        {value.length < max && (
          <button type="button" className="evidence-add" onClick={() => inputRef.current && inputRef.current.click()} disabled={busy}>
            {Icon.camera({ s: 22 })}
            <span>{busy ? "处理中…" : "添加照片"}</span>
          </button>
        )}
      </div>
      <input ref={inputRef} type="file" accept="image/*" multiple style={{ display: "none" }} onChange={pick} />
    </div>
  );
}

Object.assign(window, {
  Icon, Brand, fmtDate, fmtAgo, StatusPill, Dossier,
  ToastProvider, useToast, Modal,
  fileToDataUrl, openLightbox, Lightbox, EvidenceUploader
});
