import { useEffect, useMemo, useState } from "react";
import { guardrailEvents, type GuardrailEvent } from "../api";

// 输入护栏观测：真数据(来自 guardrail_event，middleware.Guardrail 命中即落库)。
// 命中事件流(blocked 硬拦 / suspect 灰区放行→Tier2 LLM 裁决) + 计数 + 原因分布。
//
// 诚实边界:护栏规则(Tier1 正则/敏感词 + Tier2 LLM 分类器)目前是中间件里的代码常量,
// 尚不支持运行时编辑(改规则需改代码重部署)。故本页只做观测,不摆"能改却不生效"的假配置面。
// 若要规则运行时可配,需另建配置存储 + 中间件读库(参考 prompt 控制面的热下发)。

const KIND_BADGE: Record<string, string> = { blocked: "bg-rose-50 text-rose-600", suspect: "bg-amber-50 text-amber-600" };
const KIND_LABEL: Record<string, string> = { blocked: "硬拦截", suspect: "灰区放行" };

// 解析 signals（JSON 数组字符串）为可读标签。
function signalLabels(raw: string): string[] {
  if (!raw) return [];
  try {
    const arr = JSON.parse(raw);
    return Array.isArray(arr) ? arr.map(String) : [];
  } catch {
    return [raw];
  }
}

export function GuardrailsPage() {
  const [events, setEvents] = useState<GuardrailEvent[]>([]);
  const [filter, setFilter] = useState<"" | "blocked" | "suspect">("");
  const [loading, setLoading] = useState(true);
  const [err, setErr] = useState("");

  const load = () => {
    setLoading(true);
    guardrailEvents(100)
      .then((r) => {
        setEvents(r);
        setErr("");
      })
      .catch((e) => setErr((e as Error).message))
      .finally(() => setLoading(false));
  };
  useEffect(load, []);

  const blocked = events.filter((e) => e.kind === "blocked").length;
  const suspect = events.filter((e) => e.kind === "suspect").length;
  // 原因/信号 Top（真实命中分布）。
  const topReasons = useMemo(() => {
    const m = new Map<string, number>();
    for (const e of events) {
      const keys = e.kind === "blocked" ? [e.reason || "未标注"] : signalLabels(e.signals);
      for (const k of keys.length ? keys : ["未标注"]) m.set(k, (m.get(k) ?? 0) + 1);
    }
    return [...m.entries()].sort((a, b) => b[1] - a[1]).slice(0, 6);
  }, [events]);

  const shown = filter ? events.filter((e) => e.kind === filter) : events;

  if (loading) return <div className="text-sm text-gray-400">加载护栏事件中…</div>;
  if (err) return <div className="text-sm text-rose-500">护栏事件加载失败：{err}</div>;

  return (
    <div className="space-y-6">
      <div className="flex flex-wrap items-center justify-between gap-3">
        <div>
          <h2 className="text-base font-semibold text-gray-800">输入护栏</h2>
          <p className="text-xs text-gray-400">命中事件流 · 硬拦截与灰区放行 · 原因分布（全平台真实命中）</p>
        </div>
        <button onClick={load} className="rounded-lg border border-gray-200 px-3 py-1.5 text-xs text-gray-500 hover:bg-gray-50">刷新</button>
      </div>

      {/* 计数 */}
      <div className="grid grid-cols-2 gap-4 lg:grid-cols-3">
        <Stat label="硬拦截" value={String(blocked)} tone="rose" sub="Tier1 命中即拒（近 100 条内）" />
        <Stat label="灰区放行" value={String(suspect)} tone="amber" sub="打标 → Tier2 LLM 执行前裁决" />
        <Stat label="命中总数" value={String(events.length)} tone="violet" sub="近 100 条护栏事件" />
      </div>

      {/* 规则说明（诚实：规则在代码里，非运行时可配） */}
      <div className="rounded-xl border border-gray-100 bg-white p-5 shadow-sm">
        <h4 className="mb-2 text-sm font-semibold text-gray-700">护栏规则</h4>
        <div className="space-y-1.5 text-xs leading-relaxed text-gray-500">
          <p><span className="font-medium text-gray-700">Tier1（中间件，同步硬拦）</span>：注入正则 + 敏感词库命中即 <span className="text-rose-600">blocked</span> 拒绝请求。</p>
          <p><span className="font-medium text-gray-700">Tier2（灰区升级）</span>：疑似输入打标 <span className="text-amber-600">suspect</span> 放行，由 Dispatcher 执行前调 LLM 分类器裁决。</p>
          <p className="text-gray-400">规则当前定义在网关中间件代码中，尚不支持运行时编辑（改规则需改代码重部署）。运行时可配需另建配置存储 + 中间件读库，参考「提示词」控制面的热下发。</p>
        </div>
      </div>

      {/* 原因分布 */}
      {topReasons.length > 0 && (
        <div className="rounded-xl border border-gray-100 bg-white p-5 shadow-sm">
          <h4 className="mb-3 text-sm font-semibold text-gray-700">命中原因 Top</h4>
          <div className="space-y-2">
            {topReasons.map(([reason, n]) => (
              <div key={reason} className="flex items-center gap-3">
                <div className="w-48 shrink-0 truncate text-xs text-gray-600" title={reason}>{reason}</div>
                <div className="h-2 flex-1 overflow-hidden rounded-full bg-gray-100">
                  <div className="h-full rounded-full bg-violet-400" style={{ width: `${(n / topReasons[0][1]) * 100}%` }} />
                </div>
                <div className="w-8 text-right text-xs tabular-nums text-gray-500">{n}</div>
              </div>
            ))}
          </div>
        </div>
      )}

      {/* 事件流 */}
      <div className="rounded-xl border border-gray-100 bg-white p-5 shadow-sm">
        <div className="mb-3 flex items-center justify-between">
          <h4 className="text-sm font-semibold text-gray-700">命中事件流</h4>
          <div className="flex overflow-hidden rounded-lg border border-gray-200 text-xs">
            {([["", "全部"], ["blocked", "硬拦截"], ["suspect", "灰区"]] as const).map(([v, label]) => (
              <button key={v} onClick={() => setFilter(v)}
                className={`px-3 py-1.5 ${filter === v ? "bg-violet-600 text-white" : "bg-white text-gray-500 hover:bg-gray-50"}`}>
                {label}
              </button>
            ))}
          </div>
        </div>
        {shown.length === 0 ? (
          <div className="py-8 text-center text-xs text-gray-400">暂无护栏命中 —— 没有可疑输入</div>
        ) : (
          <div className="max-h-96 overflow-auto">
            <table className="w-full text-sm">
              <thead className="sticky top-0 bg-white">
                <tr className="border-b border-gray-100 text-left text-[11px] uppercase tracking-wide text-gray-400">
                  <th className="py-2 pr-3 font-medium">时间</th>
                  <th className="py-2 pr-3 font-medium">类型</th>
                  <th className="py-2 pr-3 font-medium">原因 / 命中信号</th>
                  <th className="py-2 pr-3 font-medium">路径</th>
                  <th className="py-2 font-medium">来源</th>
                </tr>
              </thead>
              <tbody>
                {shown.map((e) => (
                  <tr key={e.id} className="border-b border-gray-50 last:border-0">
                    <td className="py-2 pr-3 text-xs text-gray-500">{new Date(e.at).toLocaleString("zh-CN")}</td>
                    <td className="py-2 pr-3">
                      <span className={`rounded px-1.5 py-0.5 text-[10px] ${KIND_BADGE[e.kind] ?? "bg-gray-100 text-gray-500"}`}>{KIND_LABEL[e.kind] ?? e.kind}</span>
                    </td>
                    <td className="py-2 pr-3">
                      {e.kind === "blocked" ? (
                        <span className="text-xs text-gray-700">{e.reason || "—"}</span>
                      ) : (
                        <div className="flex flex-wrap gap-1">
                          {signalLabels(e.signals).map((sig, i) => (
                            <span key={i} className="rounded bg-amber-50 px-1.5 py-0.5 text-[10px] text-amber-700">{sig}</span>
                          ))}
                          {signalLabels(e.signals).length === 0 && <span className="text-xs text-gray-400">—</span>}
                        </div>
                      )}
                    </td>
                    <td className="py-2 pr-3 font-mono text-[11px] text-gray-500">{e.method} {e.path}</td>
                    <td className="py-2 text-[11px] text-gray-400">{e.actor || e.ip || "匿名"}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}
      </div>
    </div>
  );
}

const TONE: Record<string, string> = { rose: "text-rose-500", amber: "text-amber-600", violet: "text-violet-600" };
function Stat({ label, value, sub, tone }: { label: string; value: string; sub?: string; tone: string }) {
  return (
    <div className="rounded-xl border border-gray-100 bg-white p-4 shadow-sm">
      <div className="text-xs text-gray-400">{label}</div>
      <div className={`mt-1 text-2xl font-semibold tabular-nums ${TONE[tone] ?? "text-gray-800"}`}>{value}</div>
      {sub && <div className="mt-1 text-[11px] text-gray-400">{sub}</div>}
    </div>
  );
}
