/* ZY console — 仓储 COO：异常巡检 cockpit（warehouse_coo_cycle.v1）+ JST 库存快照 / 运费上传。 */
const DSWH = window.ZYDesignSystem_d746df;

const WH_SEV = { P0: { tone: "danger", rank: 0 }, P1: { tone: "danger", rank: 1 }, P2: { tone: "warn", rank: 2 }, P3: { tone: "neutral", rank: 3 } };
const WH_CAT = { negative_stock: "主仓负库存", low_stock: "低库存", inventory_risk: "库存风险", warehouse_exception: "仓库异常", shipping_exception: "发货异常" };
const WH_CAT_TONE = { negative_stock: "danger", low_stock: "warn", inventory_risk: "warn", warehouse_exception: "accent", shipping_exception: "accent" };
// 仓储反馈动作集（contract: hermes-feedback-ui-contract.md · kind 照表）
const WH_FB_OPTS = [
  { kind: "normal", effect: "suppression", label: "这是正常的，别再报" },
  { kind: "resolved", effect: "resolution", label: "已解决：记录处理方式" },
  { kind: "note", effect: "knowledge", label: "记一笔（其它）" },
];

function KitWarehouse() {
  const { Card, SectionHead, Pill, Icon, Button } = DSWH;
  const K = window.KIT, S = window.KitStrip, Kp = window.KitKpi;
  const C = K.whCycle;

  const [catf, setCatf] = React.useState("全部");
  const [showSupp, setShowSupp] = React.useState(false);
  const [adj, setAdj] = React.useState(null);      // alert in adjust flow
  const [fb, setFb] = React.useState(null);        // alert in feedback
  const [feedback, setFeedback] = React.useState({}); // alert.id -> { kind, effect, text, label }（乐观态：明日生效）
  const [adjusted, setAdjusted] = React.useState({}); // id -> adjustment_ref
  const [restored, setRestored] = React.useState({}); // sku_id -> restored from learned suppression
  const [q, setQ] = React.useState("");

  const alerts = C.alerts.sort((x, y) => (WH_SEV[x.severity].rank - WH_SEV[y.severity].rank));
  const cats = ["全部", ...Object.keys(C.by_category).filter(k => C.by_category[k] > 0).map(k => WH_CAT[k])];
  const list = catf === "全部" ? alerts : alerts.filter(a => WH_CAT[a.problem_code] === catf);
  const negN = alerts.filter(a => a.problem_code === "negative_stock").length;
  const lowN = alerts.filter(a => a.problem_code === "low_stock").length;
  const LEARN = C.learning_applied || {};
  // 已被学习抑制：读模型 suppressed[]（去掉已恢复）+ 本次反馈乐观抑制（明日生效）
  const rmSupp = C.suppressed.filter(s => !restored[s.sku_id]);
  const optimisticSupp = Object.entries(feedback)
    .filter(([id, f]) => f.effect === "suppression")
    .map(([id, f]) => { const a = C.alerts.find(x => x.id === id) || {}; return { id, sku_id: a.sku_id, sku_name: a.sku_name, problem: a.problem, suppressed_reason: f.text || f.label, pending: true }; });
  const suppActive = [...optimisticSupp, ...rmSupp];

  const adjustFlowFor = (a) => ({
    icon: "PackageCheck", title: "调整库存（在手数）",
    subject: a.sku_id + " · " + a.sku_name + " · " + a.warehouse_name,
    input: { label: "目标在手数", unit: "件", initial: a.current_qty == null ? 0 : Math.max(0, a.current_qty) },
    blocked: (v) => a.source_unavailable ? true : (v === "" || Number(v) === a.current_qty),
    blockedReason: (v) => a.source_unavailable ? "数据源不可用，请先在 JST 核实，不可凭此数写入。" : "目标在手数与当前一致，无需写入。",
    previewBanner: "预览 · 未写入（尚未写入 JST）",
    previewRows: (v) => [
      { k: "当前在手数", v: a.current_qty == null ? "未知" : a.current_qty + " 件", tone: a.current_qty != null && a.current_qty < 0 ? "danger" : "" },
      { k: "目标在手数", v: v === "" ? "—" : v + " 件" },
      { k: "变动", v: (a.current_qty == null || v === "") ? "—" : (Number(v) - a.current_qty > 0 ? "+" : "") + (Number(v) - a.current_qty) + " 件", tone: (a.current_qty != null && v !== "" && Number(v) < a.current_qty) ? "danger" : "good" },
      { k: "是否写入", v: "是（本步仍为预览，未写入）", tone: "warn" },
    ],
    payload: (v) => ({ action: "inventory_adjustment", warehouse: a.warehouse_name, sku_id: a.sku_id, current_qty: a.current_qty, target_qty: v === "" ? null : Number(v) }),
    approveNote: "引擎在缺少老板审批引用时拒绝执行；调整将写入 JST 在手数并回读。",
    refPrefix: "ADJ-JST", executeLabel: "写入新在手数（写 JST）",
    receiptRows: (ref, v) => [
      { k: "在手数变化（调整前 → 调整后）", v: (a.current_qty == null ? "?" : a.current_qty) + " → " + v + " 件" },
      { k: "回读校验", v: "通过", tone: "good" },
      { k: "调整单号", v: ref },
    ],
    voidLabel: "回滚库存",
  });

  // 提交反馈 = 已审批（POST /internal/warehouse-coo/hermes/feedback）· 三元组为抑制匹配键
  const fbPayload = (a) => (opt, text) => ({
    warehouse_name: a.warehouse_name, problem_code: a.problem_code, sku_id: a.sku_id,
    kind: opt.kind, text: text || "", decision: "approve",
  });
  const submitFeedback = (a, opt, text) => {
    // POST fbPayload(a)(opt, text) -> { ok, entry } （后端 Phase D 按契约建）
    setFeedback(m => ({ ...m, [a.id]: { kind: opt.kind, effect: opt.effect, text, label: opt.label } }));
    setFb(null);
  };
  const undoFeedback = (id) => setFeedback(m => { const n = { ...m }; delete n[id]; return n; });
  const restoreSupp = (sku_id) => setRestored(r => ({ ...r, [sku_id]: true }));

  const tpl = "1.4fr 0.8fr 0.7fr 0.7fr 0.7fr 0.7fr 0.7fr 0.9fr";
  const RISK = { "正常": "good", "缺货": "warn", "负库存风险": "danger", "工厂代发在途": "neutral" };
  const rows = K.inventory.filter(r => { const s = q.trim().toLowerCase(); return !s || (r.sku + " " + r.name).toLowerCase().includes(s); });

  return (
    <div className="view viewfade">
      <Card className="ovstrip">
        <div className="ovstrip__head">
          <span className="ovstrip__orb"><Icon name="Warehouse" size={20} /></span>
          <div className="ovstrip__id">
            <div className="ovstrip__title">仓库异常巡检 · Warehouse COO<span className="ovstrip__mode"><span className="ovstrip__dot" />读优先 · 仅监控提醒</span></div>
            <div className="ovstrip__judge">每日巡检祝余文化主仓 · 只报真实风险（哪个货 / 哪个仓 / 什么问题 / 多严重 / 下一步）· 改数走受控审批</div>
          </div>
          <div className="ovstrip__src">
            <span className="ovstrip__srclabel">数据源</span>
            <span className="ovstrip__srcval">仓储 COO 巡检日报（JST 只读派生）</span>
            <Pill tone="good" soft>只读</Pill>
          </div>
        </div>
        <div className="pqhead">
          <Icon name="Siren" size={16} />
          <span className="pqhead__t">{C.headline}</span>
          <span className="pqhead__at"><Icon name="Clock" size={12} />巡检于 {C.generated_at}</span>
        </div>
      </Card>

      <div className="kpigrid">
        <Kp label="待处理告警" value={alerts.length} hint={"原始 " + C.raw_alert_count + " · 抑制 " + (C.suppressed_count)} pill="需处理" pillTone="warn" feat valTone={alerts.length ? "ovstat--warn" : ""} />
        <Kp label="主仓负库存" value={negN} hint="最急 · 在手为负" pill={negN ? "优先" : "正常"} pillTone={negN ? "danger" : "good"} valTone={negN ? "ovstat--danger" : ""} />
        <Kp label="低库存" value={lowN} hint="低于安全阈值" valTone={lowN ? "ovstat--warn" : ""} />
        <Kp label="严重度" value={Object.entries(C.by_severity).map(([k, v]) => k + "×" + v).join(" ") || "—"} hint="P0 > P1 > P2 > P3" icon="SignalHigh" />
      </div>

      <Card>
        <SectionHead title="异常告警" icon="TriangleAlert" sub="按严重度排序 · JST 派生视图 · 唯一写动作为受控库存调整（需老板审批）"
          right={<div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
            <span className="hlearn" title="applyWarehouseLearning · active_memory_total"><Icon name="Sparkles" size={12} />Hermes 已学会 {LEARN.active_memory_total || 0} 条</span>
            <div className="fchips">{cats.map(s => <button key={s} className={"fchip" + (catf === s ? " is-on" : "")} onClick={() => setCatf(s)}>{s}</button>)}</div>
          </div>} />
        <div className="whalerts">
          {list.map(a => {
            const sev = WH_SEV[a.severity] || {};
            const ref = adjusted[a.id];
            const fbState = feedback[a.id];
            const fbCtl = fbState
              ? <span className="fbdone" title={fbState.text || fbState.label}><Icon name="Sparkles" size={12} />已反馈·明日生效<button className="fbdone__u" onClick={() => undoFeedback(a.id)}>撤销</button></span>
              : <Button variant="ghost" size="sm" onClick={() => setFb(a)}>反馈</Button>;
            const hintKeys = Object.keys(a.hints || {});
            return (
              <div className={"whalert" + (a.source_unavailable ? " whalert--mute" : "") + (a.problem_code === "negative_stock" ? " whalert--neg" : "")} key={a.id}>
                <span className={"whalert__sev whalert__sev--" + sev.tone}>{a.severity}</span>
                <div className="whalert__b">
                  <div className="whalert__t">{a.sku_name}<span className="whalert__sku num">{a.sku_id}</span>
                    {a.source_unavailable && <span className="whalert__src"><Icon name="WifiOff" size={11} />数据源不可用·先核实</span>}
                  </div>
                  <div className="whalert__m">
                    <span className="whalert__wh"><Icon name="Warehouse" size={12} />{a.warehouse_name}</span>
                    <Pill tone={WH_CAT_TONE[a.problem_code]} soft>{a.problem}</Pill>
                    <span className="whalert__next"><Icon name="ArrowRight" size={12} />{a.suggested_action}</span>
                    {a.current_qty != null && <span className={"whalert__qty num" + (a.current_qty < 0 ? " ovstat--danger" : "")}>在手 {a.current_qty}</span>}
                  </div>
                  {hintKeys.length > 0 && <div className="pqhint"><Icon name="Sparkles" size={11} />历史处理：{a.hints.recommended_action_from_history}</div>}
                </div>
                <div className="whalert__act">
                  {fbCtl}
                  {ref
                    ? <span style={{ display: "inline-flex", gap: 6, alignItems: "center" }}><Pill tone="good" soft>已调整</Pill><Button variant="ghost" size="sm" onClick={() => setAdj(a)}>回执</Button></span>
                    : <Button variant="solid" size="sm" onClick={() => setAdj(a)}>调整库存</Button>}
                </div>
              </div>
            );
          })}
          {list.length === 0 && <div className="emptyrow"><Icon name="CircleCheck" size={16} />当前筛选下无异常</div>}
        </div>

        {suppActive.length > 0 && (
          <div className="whsupp">
            <button className="whsupp__h" onClick={() => setShowSupp(v => !v)}>
              <Icon name={showSupp ? "ChevronDown" : "ChevronRight"} size={14} />
              已被学习抑制 {suppActive.length} 项（反馈让明日不再报 · 可恢复）
            </button>
            {showSupp && (
              <div className="whsupp__list">
                {suppActive.map((s, i) => (
                  <div className="whsupp__row" key={s.suppressed_by_memory || s.id || i}>
                    <span className="whsupp__sku"><b>{s.sku_name}</b> <span className="num">{s.sku_id}</span> · {s.problem}{s.pending && <span className="hsupp__pend">明日生效</span>}</span>
                    <span className="whsupp__why">{s.suppressed_reason}{s.suppressed_by_memory && <span className="num"> · {s.suppressed_by_memory}</span>}</span>
                    {s.pending
                      ? <Button variant="ghost" size="sm" onClick={() => undoFeedback(s.id)}>撤销</Button>
                      : <Button variant="ghost" size="sm" onClick={() => restoreSupp(s.sku_id)}>恢复</Button>}
                  </div>
                ))}
              </div>
            )}
          </div>
        )}
      </Card>

      <Card>
        <SectionHead title="库存快照" icon="Warehouse" sub="来源 JST · 锁库存 / 在途 / 次品 / 退货 · 工厂代发负数不计为缺货"
          right={<div className="dtsearch">
            <Icon name="Search" size={14} />
            <input className="dtsearch__i" value={q} placeholder="搜 SKU / 品名" onChange={e => setQ(e.target.value)} />
            {q && <button className="dtsearch__x" onClick={() => setQ("")} title="清除"><Icon name="X" size={13} /></button>}
          </div>} />
        <div className="dtscroll"><div className="dt" style={{ minWidth: 900 }}>
          <div className="dt__head" style={{ gridTemplateColumns: tpl }}><span>SKU</span><span>仓库</span><span>库存</span><span>可用</span><span>锁库存</span><span>在途</span><span>次品</span><span>风险</span></div>
          {rows.map(r => (
            <div className={"dt__row" + (r.risk === "负库存风险" ? " is-bad" : "")} style={{ gridTemplateColumns: tpl }} key={r.sku}>
              <span className="dt__name"><b>{r.sku}</b><span className="dt__sub">{r.name}</span></span>
              <span className="dt__sub2">{r.wh}</span>
              <span className="num">{r.stock}</span>
              <span className={"num" + (r.avail < r.safe ? " ovstat--warn" : "")}>{r.avail}</span>
              <span className="num">{r.locked}</span>
              <span className="num">{r.transit}</span>
              <span className="num">{r.defect}</span>
              <span><Pill tone={RISK[r.risk]} soft>{r.risk}</Pill></span>
            </div>
          ))}
          {rows.length === 0 && <div className="emptyrow"><Icon name="PackageSearch" size={14} />无匹配 SKU</div>}
        </div></div>
      </Card>

      {adj && <window.KitGatedWrite flow={adjustFlowFor(adj)} existingRef={adjusted[adj.id]} onClose={() => setAdj(null)}
        onExecuted={(ref) => setAdjusted(o => ({ ...o, [adj.id]: ref }))}
        onVoided={() => { setAdjusted(o => { const n = { ...o }; delete n[adj.id]; return n; }); setAdj(null); }} />}
      {fb && <window.KitHermesFeedback item={fb} title="Hermes 反馈 · 仓库告警" endpoint="/internal/warehouse-coo/hermes/feedback" options={WH_FB_OPTS} payloadFor={fbPayload(fb)} onClose={() => setFb(null)} onSubmit={submitFeedback} />}
    </div>
  );
}

function KitFreight({ onAct }) {
  const { Card, SectionHead, Pill, Button } = DSWH;
  const K = window.KIT, S = window.KitStrip, Kp = window.KitKpi;
  const TONE = { "已写入": "good", "待复核": "warn" };
  const pending = K.freightBatches.filter(b => b.status === "待复核").length;
  const impact = K.freightBatches.reduce((s, b) => s + b.profitDelta, 0);
  const tpl = "1fr 0.8fr 0.7fr 0.8fr 1fr 0.8fr 0.9fr";
  const writeFreight = (b) => onAct({
    title: "写入运费并重算本月", subject: b.batch + " · " + b.month, phrase: "确认写入", submitLabel: "写入运费并重算", source: "运费批次读模型",
    preflight: "只解析运费批次并比对订单，不改其它字段。",
    preview: [{ k: "匹配行数", v: b.matched + " / " + b.rows }, { k: "批次状态", from: b.status, to: "已写入" }, { k: "本月利润影响（老板口径）", v: window.yuan(b.profitDelta) }],
    wont: ["不会改销售额", "不会改成本口径", "只写运费并触发重算"], forbidden: ["未登录提交", "非白名单接口"], rollbackNote: "重算可回滚到上一个月度快照",
  });
  return (
    <div className="view viewfade">
      <S icon="Upload" title="运费上传" mode="仓储监督" judge="写入运费批次并重算本月 · 运费支出计入老板口径利润（员工口径提成不承担运费）" src="运费批次读模型" />
      <div className="kpigrid">
        <Kp label="本月批次" value={K.freightBatches.length} hint="运费上传批次" icon="Upload" />
        <Kp label="待复核" value={pending} hint="匹配未完成" pill={pending ? "需复核" : "正常"} pillTone={pending ? "warn" : "good"} feat />
        <Kp label="累计利润影响" value={window.yuan(impact)} hint="老板口径 · 运费支出计入（员工口径提成不承担运费）" valTone={impact < 0 ? "ovstat--danger" : ""} />
        <Kp label="写入方式" value="受控" hint="预检 → 写入 → 重算" icon="ShieldCheck" iconColor="var(--accent)" />
      </div>
      <Card>
        <SectionHead title="运费批次" icon="Upload" sub="待复核批次走受控动作写入并重算" />
        <div className="dtscroll"><div className="dt" style={{ minWidth: 720 }}>
          <div className="dt__head" style={{ gridTemplateColumns: tpl }}><span>批次</span><span>月份</span><span>行数</span><span>匹配</span><span>利润影响（老板口径）</span><span>状态</span><span></span></div>
          {K.freightBatches.map(b => (
            <div className="dt__row" style={{ gridTemplateColumns: tpl }} key={b.batch}>
              <span className="dt__id num">{b.batch}</span>
              <span className="dt__sub2 num">{b.month}</span>
              <span className="num">{b.rows}</span>
              <span className={"num" + (b.matched < b.rows ? " ovstat--warn" : "")}>{b.matched}</span>
              <span className="num">{window.yuan(b.profitDelta)}</span>
              <span><Pill tone={TONE[b.status]} soft>{b.status}</Pill></span>
              <span style={{ textAlign: "right" }}>{b.status === "待复核" && <Button variant="solid" size="sm" onClick={() => writeFreight(b)}>写入重算</Button>}</span>
            </div>
          ))}
        </div></div>
      </Card>
    </div>
  );
}

Object.assign(window, { KitWarehouse, KitFreight });
