import React from "react";
import { fmtClock, fmtDate } from "./time";
import { LoadingTitle } from "./loading-indicator";
import { errorMessage, requestJson } from "./unidesk-error";
import { UniDeskErrorBanner } from "./unidesk-error-banner";

type AnyRecord = Record<string, any>;

const h = React.createElement;
const { useEffect, useMemo } = React;
const useState: any = React.useState;

function StatusBadge({ status, children, title }: AnyRecord) {
  const normalized = String(status || "unknown").toLowerCase();
  return h("span", { className: `status-badge ${normalized}`, title }, children || status || "unknown");
}

function MetricCard({ label, value, hint, tone }: AnyRecord) {
  return h("article", { className: `metric-card ${tone || ""}` },
    h("div", { className: "metric-label" }, label),
    h("div", { className: "metric-value" }, value),
    h("div", { className: "metric-hint" }, hint),
  );
}

function Panel({ title, eyebrow, actions, children, className, loading }: AnyRecord) {
  return h("section", { className: `panel ${className || ""}` },
    h("div", { className: "panel-head" },
      h("div", null,
        eyebrow ? h("p", { className: "panel-eyebrow" }, eyebrow) : null,
        h(LoadingTitle, { title, loading }),
      ),
      actions ? h("div", { className: "panel-actions" }, actions) : null,
    ),
    h("div", { className: "panel-body" }, children),
  );
}

function RawButton({ title, data, onOpen, testId }: AnyRecord) {
  return h("button", {
    type: "button",
    className: "ghost-btn",
    "data-testid": testId,
    onClick: () => onOpen?.(title, data),
  }, "查看原始JSON");
}

function EmptyState({ title, text }: AnyRecord) {
  return h("div", { className: "empty-state" }, h("strong", null, title), h("span", null, text));
}

function objectRecord(value: any): AnyRecord | null {
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
}

function asArray(value: any): any[] {
  return Array.isArray(value) ? value : [];
}

function fmtCount(value: any): string {
  const number = Number(value);
  return Number.isFinite(number) ? number.toLocaleString("zh-CN") : "--";
}

function compactText(value: any, max = 140): string {
  if (value === null || value === undefined) return "--";
  const text = typeof value === "string" ? value : JSON.stringify(value);
  const normalized = String(text || "").replace(/\s+/gu, " ").trim();
  return normalized.length > max ? `${normalized.slice(0, max - 1)}...` : normalized || "--";
}

function eventTags(event: any): string[] {
  return asArray(event?.tags).map((tag) => String(tag || "").trim()).filter(Boolean);
}

function statNumber(value: any): number {
  const number = Number(value);
  return Number.isFinite(number) && number >= 0 ? Math.floor(number) : 0;
}

function microserviceRuntime(service: any): AnyRecord {
  return service?.runtime && typeof service.runtime === "object" && !Array.isArray(service.runtime) ? service.runtime : {};
}

function microserviceBackend(service: any): AnyRecord {
  return service?.backend && typeof service.backend === "object" && !Array.isArray(service.backend) ? service.backend : {};
}

function normalizeTagFilter(value: string): string {
  return String(value || "")
    .split(/[\s,]+/u)
    .map((tag) => tag.trim())
    .filter(Boolean)
    .join(",");
}

function oaProxy(apiBaseUrl: string, path: string): string {
  return `${apiBaseUrl}/microservices/oa-event-flow/proxy${path}`;
}

function eventTypeTone(type: string): string {
  if (type.includes("error") || type.includes("failed")) return "failed";
  if (type.includes("stats")) return "ok";
  if (type.includes("step") || type.includes("updated")) return "running";
  return "queued";
}

function statScopeLabel(stat: any): string {
  const kind = String(stat?.subjectKind || "trace");
  const id = String(stat?.subjectId || stat?.scopeId || "");
  return id ? `${kind}:${id}` : String(stat?.scopeId || "--");
}

function EventTagRail({ tags }: AnyRecord) {
  const visible = eventTags({ tags }).slice(0, 6);
  return h("div", { className: "oa-tag-rail" },
    visible.length === 0 ? h("span", { className: "muted" }, "--") : visible.map((tag) => h("code", { key: tag }, tag)),
  );
}

function EventTable({ events, onRaw }: AnyRecord) {
  const rows = [...asArray(events)].reverse();
  return rows.length === 0
    ? h(EmptyState, { title: "事件表暂无记录", text: "等待 Code Queue 或 Pipeline 按 tag 发布 OA 事件" })
    : h("div", { className: "table-wrap oa-event-table-wrap" }, h("table", { className: "oa-event-table", "data-testid": "oa-event-flow-event-table" },
      h("thead", null, h("tr", null,
        h("th", null, "Seq"),
        h("th", null, "Type"),
        h("th", null, "Source"),
        h("th", null, "Aggregate"),
        h("th", null, "Tags"),
        h("th", null, "Payload"),
        h("th", null, "Created"),
        h("th", null, "Raw"),
      )),
      h("tbody", null, rows.map((event: any) => {
        const type = String(event?.type || "event");
        const aggregate = `${String(event?.aggregateType || "--")}:${String(event?.aggregateId || "--")}`;
        return h("tr", { key: event?.eventId || event?.sequence },
          h("td", null, h("code", null, fmtCount(event?.sequence))),
          h("td", null, h(StatusBadge, { status: eventTypeTone(type) }, type)),
          h("td", null, h("strong", null, event?.sourceId || "--"), h("code", null, event?.sourceKind || "--")),
          h("td", null, h("code", null, aggregate)),
          h("td", null, h(EventTagRail, { tags: event?.tags })),
          h("td", null, h("span", { className: "oa-payload-preview" }, compactText(event?.payload, 180))),
          h("td", null, fmtDate(event?.createdAt)),
          h("td", null, h(RawButton, { title: `OA Event ${event?.sequence || ""}`, data: event, onOpen: onRaw, testId: `raw-oa-event-${event?.sequence || "unknown"}` })),
        );
      })),
    ));
}

function StatsTable({ stats, onRaw }: AnyRecord) {
  const rows = asArray(stats);
  return rows.length === 0
    ? h(EmptyState, { title: "统计中心暂无投影", text: "trace-stats-snapshot / trace-step-created 进入事件流后会更新这里" })
    : h("div", { className: "table-wrap oa-stats-table-wrap" }, h("table", { className: "oa-stats-table", "data-testid": "oa-event-flow-stats" },
      h("thead", null, h("tr", null,
        h("th", null, "Scope"),
        h("th", null, "Service"),
        h("th", null, "STEP"),
        h("th", null, "Read"),
        h("th", null, "Edit"),
        h("th", null, "Run"),
        h("th", null, "Error"),
        h("th", null, "Output Seq"),
        h("th", null, "Revision"),
        h("th", null, "Updated"),
        h("th", null, "Raw"),
      )),
      h("tbody", null, rows.map((stat: any) => h("tr", { key: stat?.scopeId || `${stat?.serviceId}-${stat?.subjectId}` },
        h("td", null, h("strong", null, statScopeLabel(stat)), h("code", null, stat?.scopeId || "--")),
        h("td", null, h(StatusBadge, { status: String(stat?.serviceId || "unknown") === "code-queue" ? "running" : "queued" }, stat?.serviceId || "--")),
        h("td", null, h("strong", null, fmtCount(statNumber(stat?.stepCount ?? stat?.llmStepCount)))),
        h("td", null, fmtCount(statNumber(stat?.readCount))),
        h("td", null, fmtCount(statNumber(stat?.editCount))),
        h("td", null, fmtCount(statNumber(stat?.runCount))),
        h("td", null, fmtCount(statNumber(stat?.errorCount))),
        h("td", null, h("code", null, fmtCount(statNumber(stat?.outputMaxSeq)))),
        h("td", null, fmtCount(statNumber(stat?.statsRevision))),
        h("td", null, fmtDate(stat?.updatedAt)),
        h("td", null, h(RawButton, { title: `OA Trace Stats ${stat?.scopeId || ""}`, data: stat, onOpen: onRaw, testId: `raw-oa-stats-${String(stat?.scopeId || "unknown").replace(/[^a-zA-Z0-9_-]/gu, "_")}` })),
      ))),
    ));
}

export function OaEventFlowPage({ microservices, onRaw, apiBaseUrl = "/api" }: AnyRecord) {
  const service = microservices.find((item: any) => item.id === "oa-event-flow") || null;
  const [tagFilter, setTagFilter] = useState("service:code-queue");
  const [state, setState] = useState({ loading: false, error: "", health: null, diagnostics: null, events: [], stats: [], refreshedAt: null });
  const [streamState, setStreamState] = useState({ status: "idle", message: "未连接", lastEventAt: "" });
  const normalizedTags = useMemo(() => normalizeTagFilter(tagFilter), [tagFilter]);

  async function load(): Promise<void> {
    if (!service) return;
    setState((previous: any) => ({ ...previous, loading: true, error: "" }));
    try {
      const tagQuery = normalizedTags ? `tags=${encodeURIComponent(normalizedTags)}&` : "";
      const [health, diagnostics, events, stats] = await Promise.all([
        requestJson(`${apiBaseUrl}/microservices/oa-event-flow/health`, { failureFields: [] }),
        requestJson(oaProxy(apiBaseUrl, "/api/diagnostics")),
        requestJson(oaProxy(apiBaseUrl, `/api/events?${tagQuery}limit=100`)),
        requestJson(oaProxy(apiBaseUrl, `/api/stats/trace?${tagQuery}limit=100`)),
      ]);
      setState({
        loading: false,
        error: "",
        health,
        diagnostics,
        events: asArray(events?.events),
        stats: asArray(stats?.stats),
        refreshedAt: new Date(),
      });
    } catch (err) {
      setState((previous: any) => ({ ...previous, loading: false, error: errorMessage(err, "OA Event Flow 加载失败") }));
    }
  }

  useEffect(() => {
    void load();
  }, [service?.id, service?.runtime?.providerStatus, normalizedTags]);

  useEffect(() => {
    if (!service || typeof EventSource === "undefined") return undefined;
    const tagQuery = normalizedTags ? `?tags=${encodeURIComponent(normalizedTags)}` : "";
    const source = new EventSource(`${oaProxy(apiBaseUrl, "/api/events/stream")}${tagQuery}`, { withCredentials: true });
    setStreamState({ status: "running", message: "SSE connecting", lastEventAt: "" });
    const onHello = (message: MessageEvent): void => {
      setStreamState({ status: "online", message: compactText(message.data, 120), lastEventAt: new Date().toISOString() });
    };
    const onMessage = (message: MessageEvent): void => {
      try {
        const event = JSON.parse(String(message.data || "{}"));
        setStreamState({ status: "online", message: String(event?.type || message.type || "event"), lastEventAt: new Date().toISOString() });
        setState((previous: any) => {
          const events = [...asArray(previous.events).filter((row: any) => String(row?.eventId || "") !== String(event?.eventId || "")), event]
            .sort((left: any, right: any) => Number(left?.sequence || 0) - Number(right?.sequence || 0))
            .slice(-100);
          const stats = event?.type === "trace-stats-updated" && objectRecord(event?.payload?.stats)
            ? [event.payload.stats, ...asArray(previous.stats).filter((row: any) => String(row?.scopeId || "") !== String(event.payload.stats.scopeId || ""))].slice(0, 100)
            : previous.stats;
          return { ...previous, events, stats };
        });
      } catch (err) {
        setStreamState({ status: "warn", message: errorMessage(err, "SSE 事件解析失败"), lastEventAt: new Date().toISOString() });
      }
    };
    const onError = (): void => {
      setStreamState((previous: any) => ({ ...previous, status: "warn", message: "SSE reconnecting" }));
    };
    source.addEventListener("hello", onHello);
    source.addEventListener("task-updated", onMessage);
    source.addEventListener("queue-updated", onMessage);
    source.addEventListener("trace-step-created", onMessage);
    source.addEventListener("trace-stats-snapshot", onMessage);
    source.addEventListener("trace-stats-updated", onMessage);
    source.addEventListener("trace-error", onMessage);
    source.onerror = onError;
    return () => source.close();
  }, [service?.id, apiBaseUrl, normalizedTags]);

  if (!service) return h(EmptyState, { title: "OA Event Flow 未登记", text: "请在 config.json 的 microservices 中登记 id=oa-event-flow" });

  const runtime = microserviceRuntime(service);
  const backend = microserviceBackend(service);
  const diagnostics = state.diagnostics || {};
  const health = state.health || {};
  const eventCount = diagnostics.eventCount ?? health.eventCount;
  const statsCount = diagnostics.traceStatsCount ?? health.traceStatsCount;
  const latestSequence = diagnostics.latestSequence ?? health.latestSequence;
  const pipelineBridge = diagnostics.pipelineBridge || health.pipelineBridge || {};
  const eventTypes = asArray(diagnostics.eventTypes).slice(0, 8);

  return h("div", { className: "oa-event-flow-page", "data-testid": "oa-event-flow-page" },
    h(Panel, {
      title: "OA Event Flow 控制台",
      eyebrow: "Unified OA Event Bus + Stats Projection",
      loading: state.loading,
      actions: h("div", { className: "panel-actions" },
        h("button", { type: "button", className: "ghost-btn", onClick: load, disabled: state.loading, "data-testid": "oa-event-flow-refresh" }, state.loading ? "刷新中" : "刷新"),
        h(RawButton, { title: "OA Event Flow Service", data: service, onOpen: onRaw, testId: "raw-oa-event-flow-service" }),
      ),
    },
      h("div", { className: "oa-flow-hero" },
        h("div", null,
          h("div", { className: "node-version-line" },
            h(StatusBadge, { status: health?.ok || runtime.providerStatus === "online" ? "online" : "warn" }, health?.ok ? "HEALTH OK" : runtime.providerStatus || "unknown"),
            h(StatusBadge, { status: streamState.status }, streamState.status.toUpperCase()),
            h("span", null, backend.public ? "公网暴露" : "仅 UniDesk frontend 代理访问"),
          ),
          h("p", { className: "muted paragraph" }, "独立事件流微服务统一承载 Code Queue 与 Pipeline 的事件发布、tag 订阅、事件表审计和 Trace/STEP 统计投影。"),
        ),
        h("div", { className: "oa-flow-signal" },
          h("span", null, "stream"),
          h("strong", null, streamState.message || "--"),
          h("code", null, streamState.lastEventAt ? fmtClock(new Date(streamState.lastEventAt)) : "waiting"),
        ),
      ),
      h(UniDeskErrorBanner, { error: state.error, wide: true }),
    ),
    h("div", { className: "oa-flow-metrics" },
      h(MetricCard, { label: "事件总量", value: fmtCount(eventCount), hint: `latest seq ${fmtCount(latestSequence)}`, tone: "ok" }),
      h(MetricCard, { label: "Trace Stats", value: fmtCount(statsCount), hint: "oa_trace_stats 投影" }),
      h(MetricCard, { label: "SSE Clients", value: fmtCount(health?.sseClientCount ?? asArray(diagnostics.sseClients).length), hint: streamState.message || "tag subscription" }),
      h(MetricCard, { label: "Pipeline Bridge", value: pipelineBridge?.enabled ? fmtCount(pipelineBridge?.insertedCount) : "OFF", hint: pipelineBridge?.lastError || pipelineBridge?.lastFinishedAt || `${pipelineBridge?.mode || "snapshot"} service:pipeline` }),
      h(MetricCard, { label: "DB", value: health?.databaseReady || diagnostics.databaseReady ? "READY" : "WAIT", hint: health?.databaseLastError || diagnostics.databaseLastError || "PostgreSQL persisted" }),
    ),
    h(Panel, { title: "标签订阅", eyebrow: state.refreshedAt ? `Updated ${fmtClock(state.refreshedAt)}` : "Tag Pub/Sub" },
      h("div", { className: "oa-filter-bar" },
        h("label", null, h("span", null, "tags"), h("input", { value: tagFilter, onChange: (event: any) => setTagFilter(event.target.value), placeholder: "service:code-queue, trace", "data-testid": "oa-event-flow-tag-filter" })),
        h("div", { className: "oa-filter-presets" },
          h("button", { type: "button", className: "ghost-btn", onClick: () => setTagFilter("service:code-queue") }, "Code Queue"),
          h("button", { type: "button", className: "ghost-btn", onClick: () => setTagFilter("service:pipeline") }, "Pipeline"),
          h("button", { type: "button", className: "ghost-btn", onClick: () => setTagFilter("trace") }, "Trace"),
          h("button", { type: "button", className: "ghost-btn", onClick: () => setTagFilter("") }, "All"),
        ),
        h("code", null, normalizedTags || "all events"),
      ),
      h("div", { className: "oa-type-strip" },
        eventTypes.length === 0 ? h("span", { className: "muted" }, "等待事件类型统计") : eventTypes.map((row: any) => h("span", { key: row.type, className: "data-chip" }, `${row.type} ${fmtCount(row.count)}`)),
      ),
    ),
    h("div", { className: "oa-flow-grid" },
      h(Panel, { title: "事件表", eyebrow: "oa_events persisted log", className: "oa-flow-wide", loading: state.loading,
        actions: h(RawButton, { title: "OA Event Query", data: { events: state.events, diagnostics }, onOpen: onRaw, testId: "raw-oa-events" }),
      }, h(EventTable, { events: state.events, onRaw })),
      h(Panel, { title: "统计中心", eyebrow: "oa_trace_stats read model", className: "oa-flow-wide", loading: state.loading,
        actions: h(RawButton, { title: "OA Trace Stats", data: state.stats, onOpen: onRaw, testId: "raw-oa-trace-stats" }),
      }, h(StatsTable, { stats: state.stats, onRaw })),
    ),
  );
}
