/* ZY console — 共享受控写动作组件：
   KitGatedWrite —— dry-run 预览 → 老板审批（审批引用 + 老板写入口令）→ 执行(写 JST) → 回执 → 回滚。
   KitHermesFeedback —— Hermes 反馈（advisory）：一键提交即审批，明日生效；不写 JST。
   采购 COO（生成采购单）与仓储 COO（调整库存）共用，flow 描述差异。

   C1（2026-06-30 审计 + docs/architecture/2026-06-30-cockpit-controlled-write-auth-contract.md）：
   任何真实受控写的「执行」步都必须要求一个「老板写入口令」——登录会话 ≠ 可执行写。
   下面的 <input> 只存在组件内存状态（React.useState），绝不写 localStorage / 打包产物 /
   console；关闭弹窗即清空。真实后端对应请求头 x-boss-write-token（本 Kit 是设计原型，不发真实网络请求，
   但字段命名对齐，方便工程落地时直接映射）。 */
const DSCTRL = window.ZYDesignSystem_d746df;

function KitGatedWrite({ flow, existingRef, onClose, onExecuted, onVoided }) {
  const { Icon } = DSCTRL;
  const hasInput = !!flow.input;
  const [val, setVal] = React.useState(hasInput ? flow.input.initial : null);
  const [form, setForm] = React.useState(() => {
    const m = {}; (flow.fields || []).forEach(f => { m[f.key] = f.value; }); return m;
  });
  const setField = (k, v) => setForm(f => ({ ...f, [k]: v }));
  const [lines, setLines] = React.useState(() => flow.lines ? flow.lines.initial.map(l => ({ ...l })) : null);
  const setLine = (i, k, v) => setLines(ls => ls.map((l, j) => j === i ? { ...l, [k]: v } : l));
  const addLine = () => setLines(ls => [...ls, { ...(flow.lines.blank || {}) }]);
  const delLine = (i) => setLines(ls => ls.length > 1 ? ls.filter((_, j) => j !== i) : ls);
  const [step, setStep] = React.useState(existingRef ? 2 : 0);
  const [ref, setRef] = React.useState(existingRef || null);
  const [voided, setVoided] = React.useState(false);
  const [showPayload, setShowPayload] = React.useState(false);
  const [bossToken, setBossToken] = React.useState(""); // session-only；从不持久化
  const decisionRef = React.useMemo(() => (flow.decisionPrefix || "DEC") + "-" + String(Date.now()).slice(-8), []);
  const STEPS = flow.steps || ["预览", "审批", "执行", "回执"];

  const blocked = typeof flow.blocked === "function" ? flow.blocked(val, form, lines) : flow.blocked;
  const blockedReason = typeof flow.blockedReason === "function" ? flow.blockedReason(val, form, lines) : flow.blockedReason;
  const previewRows = flow.previewRows ? flow.previewRows(val, form, lines) : [];
  const payload = flow.payload ? flow.payload(val, form, lines) : null;
  const receiptRows = (step === 3 && flow.receiptRows) ? flow.receiptRows(ref, val, form, lines) : [];
  const canExecute = bossToken.trim().length > 0;

  const approve = () => {
    const newRef = (flow.refPrefix || "REF") + "-" + new Date().toISOString().slice(2, 10).replace(/-/g, "") + "-" + Math.floor(1000 + Math.random() * 9000);
    setRef(newRef); setStep(3); setBossToken(""); if (onExecuted) onExecuted(newRef, val, form, lines);
  };
  const doVoid = () => { setVoided(true); if (onVoided) onVoided(); };
  const close = () => { setBossToken(""); onClose(); };

  return (
    <div className="camodal__scrim" onClick={close}>
      <div className="camodal" onClick={e => e.stopPropagation()}>
        <div className="camodal__head">
          <span className="camodal__ic"><Icon name={flow.icon || "ShieldCheck"} size={20} /></span>
          <div>
            <div className="camodal__t">{flow.title}</div>
            <div className="camodal__sub">受控写动作 · 唯一写入 · {flow.subject}</div>
          </div>
          <button className="camodal__x" onClick={close}><Icon name="X" size={17} /></button>
        </div>

        <div className="camodal__body">
          <div className="postep">
            {STEPS.map((s, i) => (
              <React.Fragment key={i}>
                <span className={"postep__i" + (i <= step ? " is-done" : "") + (i === step ? " is-cur" : "")}>
                  <span className="postep__n">{i < step ? "✓" : i + 1}</span>{s}
                </span>
                {i < STEPS.length - 1 && <span className={"postep__bar" + (i < step ? " is-done" : "")} />}
              </React.Fragment>
            ))}
          </div>

          {step === 0 && (
            <React.Fragment>
              <div className="powarn powarn--ok"><Icon name="Eye" size={14} />{flow.previewBanner || "预览 · 未写入（不写 JST）"}</div>
              {hasInput && (
                <div className="poinput">
                  <span className="poinput__l">{flow.input.label}</span>
                  <span className="poinput__in">
                    <input className="poinput__i" type="number" value={val}
                      onChange={e => setVal(e.target.value === "" ? "" : Number(e.target.value))} />
                    <span className="poinput__u">{flow.input.unit}</span>
                  </span>
                </div>
              )}
              {flow.fields && flow.fields.length > 0 && (
                <div className="poform">
                  {flow.fields.map(f => (
                    <label className="poinput poinput--row" key={f.key}>
                      <span className="poinput__l">{f.label}{f.hint && <span className="poinput__hint">{f.hint}</span>}</span>
                      <span className="poinput__in">
                        {f.type === "select"
                          ? <select className="poinput__i poinput__sel" value={form[f.key]} onChange={e => setField(f.key, e.target.value)}>
                              {f.options.map(o => (typeof o === "object"
                                ? <option key={o.value} value={o.value}>{o.label}</option>
                                : <option key={o} value={o}>{o}</option>))}
                            </select>
                          : <input className="poinput__i" type={f.type === "number" ? "number" : "text"} value={form[f.key]}
                              placeholder={f.placeholder || ""}
                              onChange={e => setField(f.key, f.type === "number" ? (e.target.value === "" ? "" : Number(e.target.value)) : e.target.value)} />}
                        {f.unit && <span className="poinput__u">{f.unit}</span>}
                      </span>
                    </label>
                  ))}
                </div>
              )}
              {flow.lines && (
                <div className="polines">
                  <div className="polines__cap"><Icon name="List" size={12} />{flow.lines.label || "明细"}<span className="polines__n num">{lines.length} 款</span></div>
                  {lines.map((l, i) => (
                    <div className="polines__row" key={i}>
                      {flow.lines.columns.map(c => (
                        <span className="polines__cell" key={c.key}>
                          <input className="polines__i" type={c.type === "number" ? "number" : "text"} value={l[c.key]}
                            placeholder={c.placeholder || c.label}
                            onChange={e => setLine(i, c.key, c.type === "number" ? (e.target.value === "" ? "" : Number(e.target.value)) : e.target.value)} />
                          {c.unit && <span className="polines__u">{c.unit}</span>}
                        </span>
                      ))}
                      <button className="polines__del" onClick={() => delLine(i)} disabled={lines.length <= 1} title="删除此款"><Icon name="Trash2" size={13} /></button>
                    </div>
                  ))}
                  <button className="polines__add" onClick={addLine}><Icon name="Plus" size={13} />添加一款 SKU</button>
                </div>
              )}
              <div className="capreview">
                {previewRows.map((p, i) => (
                  <div className="capreview__r" key={i}><span className="capreview__k">{p.k}</span><span className={"capreview__v " + (p.tone ? "ovstat--" + p.tone : "")}>{p.v}</span></div>
                ))}
              </div>
              {payload && (
                <div className="popayload">
                  <button className="fbpayload__t" onClick={() => setShowPayload(v => !v)}>
                    <Icon name={showPayload ? "ChevronDown" : "ChevronRight"} size={13} />系统审计 · 技术材料
                  </button>
                  {showPayload && (
                    <React.Fragment>
                      <div className="popayload__cap"><Icon name="Code" size={12} />JST payload（dry-run · 不发送）</div>
                      <pre>{JSON.stringify(payload, null, 2)}</pre>
                    </React.Fragment>
                  )}
                </div>
              )}
              {blocked && <div className="powarn powarn--no"><Icon name="Ban" size={14} />{blockedReason}</div>}
            </React.Fragment>
          )}

          {step === 1 && (
            <React.Fragment>
              <div className="powarn powarn--warn"><Icon name="ShieldAlert" size={14} />{flow.approveNote || "未填写审批材料前，引擎拒绝执行。"}</div>
              <div className="capreview">
                <div className="capreview__r"><span className="capreview__k">审批人</span><span className="capreview__v">老板 · 决策视角</span></div>
                <div className="capreview__r"><span className="capreview__k">审批引用编号</span><span className="capreview__v num">{decisionRef}</span></div>
                <div className="capreview__r"><span className="capreview__k">执行动作</span><span className="capreview__v">{flow.executeLabel}</span></div>
              </div>
              <div className="bosstoken">
                <div className="bosstoken__l"><Icon name="Lock" size={13} />老板写入口令</div>
                <div className="bosstoken__hint">此为真实写入。需老板写入口令方可执行——登录身份不足以写入。</div>
                <input className="bosstoken__i" type="password" value={bossToken} placeholder="输入老板写入口令"
                  onChange={e => setBossToken(e.target.value)} />
              </div>
            </React.Fragment>
          )}

          {step === 2 && (
            <div className="powarn powarn--ok"><Icon name="Loader" size={14} />正在写入 JST……</div>
          )}

          {step === 3 && (
            <React.Fragment>
              {!voided ? (
                <React.Fragment>
                  <div className="powarn powarn--ok"><Icon name="CheckCheck" size={14} />已执行 · 已写入 JST 并回读校验通过</div>
                  <div className="capreview">
                    {receiptRows.map((p, i) => (
                      <div className="capreview__r" key={i}><span className="capreview__k">{p.k}</span><span className={"capreview__v " + (p.tone ? "ovstat--" + p.tone : "")}>{p.v}</span></div>
                    ))}
                  </div>
                  <div className="capreview" style={{ marginTop: 2 }}>
                    <div className="capreview__r"><span className="capreview__k">审批引用</span><span className="capreview__v num">{decisionRef}</span></div>
                  </div>
                </React.Fragment>
              ) : (
                <div className="powarn powarn--no" style={{ marginTop: 2 }}><Icon name="Undo2" size={14} />已{flow.voidLabel || "回滚"} · 写入已撤销，恢复到先前状态</div>
              )}
            </React.Fragment>
          )}
        </div>

        <div className="camodal__foot">
          <span className="camodal__note"><Icon name="ShieldCheck" size={13} />{step < 3 ? "读优先 · 审批前不写 JST" : "操作已留痕 · 可回滚"}</span>
          <span style={{ display: "flex", gap: 8 }}>
            {step === 0 && <React.Fragment><button className="btn btn--ghost" onClick={close}>取消</button><button className="btn btn--solid" disabled={blocked} onClick={() => setStep(1)}>去审批</button></React.Fragment>}
            {step === 1 && <React.Fragment><button className="btn btn--ghost" onClick={() => setStep(0)}>返回预览</button><button className="btn btn--solid" disabled={!canExecute} onClick={approve}>老板确认审批并执行</button></React.Fragment>}
            {step === 3 && !voided && <React.Fragment><button className="btn btn--danger" onClick={doVoid}>{flow.voidLabel || "回滚"}</button><button className="btn btn--ghost" onClick={close}>完成</button></React.Fragment>}
            {step === 3 && voided && <button className="btn btn--ghost" onClick={close}>关闭</button>}
          </span>
        </div>
      </div>
    </div>
  );
}

/* Hermes 反馈（advisory）—— 一键提交 = 已审批（登录用户即 reviewer，网关补 decision_ref）。
   contract: hermes-feedback-ui-contract.md。记忆在下一次日跑生效，故卡片乐观态写「明日生效」。
   反馈绝不写 JST、不下单、不改库存 —— 与老板审批的业务动作（KitGatedWrite）刻意区分，
   因此不需要老板写入口令（它不是一次业务写，是给 Hermes 的学习偏好）。 */
function KitHermesFeedback({ item, title, endpoint, options, payloadFor, onClose, onSubmit }) {
  const { Icon } = DSCTRL;
  const [kind, setKind] = React.useState(options[0].kind);
  const [text, setText] = React.useState("");
  const [showPayload, setShowPayload] = React.useState(false);
  const opt = options.find(o => o.kind === kind) || options[0];
  const EFFECT_LABEL = { suppression: "明日不再推送该项（抑制）", resolution: "记为历史处理，明日带回", supplier: "记供应商可靠度", preference: "记补货偏好", knowledge: "记一笔知识" };
  const payload = payloadFor ? payloadFor(opt, text) : { kind: opt.kind, text };
  const submit = () => onSubmit(item, opt, text.trim());
  return (
    <div className="camodal__scrim" onClick={onClose}>
      <div className="camodal camodal--sm" onClick={e => e.stopPropagation()}>
        <div className="camodal__head">
          <span className="camodal__ic camodal__ic--hermes"><Icon name="Sparkles" size={20} /></span>
          <div>
            <div className="camodal__t">{title || "Hermes 反馈"}</div>
            <div className="camodal__sub">{item.sku_name} · {item.sku_id}</div>
          </div>
          <button className="camodal__x" onClick={onClose}><Icon name="X" size={17} /></button>
        </div>
        <div className="camodal__body">
          <div className="fbadv"><Icon name="Info" size={14} /><span>教 Hermes 而已 —— 只改明日 COO <b>推送什么</b>，<b>不写 JST、不下单、不改库存</b>。你就是审批人，提交后明日生效；随时可在「已被学习抑制」里恢复。</span></div>
          <div className="fbopts">
            {options.map(o => (
              <button key={o.kind} className={"fbopt" + (kind === o.kind ? " is-on" : "")} onClick={() => setKind(o.kind)}>
                <span className="fbopt__dot" />
                <span className="fbopt__l">{o.label}</span>
              </button>
            ))}
          </div>
          <div className="fbeffect"><Icon name="ArrowRight" size={12} />{EFFECT_LABEL[opt.effect] || "记一笔"}</div>
          <textarea className="fbnote" placeholder="补一句话（可选，例如：这个款季节过了先别补）" value={text} onChange={e => setText(e.target.value)} />
          <button className="fbpayload__t" onClick={() => setShowPayload(v => !v)}>
            <Icon name={showPayload ? "ChevronDown" : "ChevronRight"} size={13} />系统审计 · 提交材料
          </button>
          {showPayload && (
            <div className="popayload">
              <div className="popayload__cap"><Icon name="Code" size={12} />审批人 / 审批引用由网关按登录会话补全，不在此发送</div>
              <pre>{JSON.stringify(payload, null, 2)}</pre>
            </div>
          )}
        </div>
        <div className="camodal__foot">
          <span className="camodal__note"><Icon name="Sparkles" size={13} />advisory · 可在抑制抽屉里恢复</span>
          <span style={{ display: "flex", gap: 8 }}>
            <button className="btn btn--ghost" onClick={onClose}>取消</button>
            <button className="btn btn--solid" onClick={submit}>提交反馈 · 明日生效</button>
          </span>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { KitGatedWrite, KitHermesFeedback });
