<!-- SPEC: PJ2026-010405 云端控制台 / Gate. -->
<!-- Implementation reference: draft-2026-07-13-p0-cloud-console. -->
<script setup lang="ts">
import { Info, RefreshCw } from "lucide-vue-next";
import { computed } from "vue";
import DataGrid from "@/components/common/DataGrid.vue";
import type { DataGridColumn } from "@/components/common/DataGrid.vue";
import Pagination from "@/components/common/Pagination.vue";
import InspectorPanel from "@/components/console/InspectorPanel.vue";
import SectionFrame from "@/components/layout/SectionFrame.vue";
import StatusStrip from "@/components/layout/StatusStrip.vue";
import type { GateDiagnosticRow, GateDiagnosticsResponse } from "@/types";

const props = defineProps<{ payload: GateDiagnosticsResponse | null; rows: GateDiagnosticRow[]; loading: boolean }>();
const query = defineModel<string>("query", { required: true });
const statusFilter = defineModel<string>("statusFilter", { required: true });
const page = defineModel<number>("page", { required: true });
const selectedRow = defineModel<GateDiagnosticRow | null>("selectedRow", { required: true });
const emit = defineEmits<{ refresh: []; select: [row: GateDiagnosticRow]; close: [] }>();
const pageSize = 8;
const columns: DataGridColumn[] = [{ key: "category", label: "类别" }, { key: "check", label: "检查项" }, { key: "status", label: "状态" }, { key: "owner", label: "归因对象" }, { key: "detail", label: "关键细节" }, { key: "actions", label: "下钻", align: "right" }];
const statusCounts = computed(() => ({ failed: props.rows.filter((row) => row.statusKey === "failed").length, blocked: props.rows.filter((row) => row.statusKey === "blocked").length, pending: props.rows.filter((row) => row.statusKey === "pending").length, pass: props.rows.filter((row) => row.statusKey === "pass").length }));
const filteredRows = computed(() => { const needle = query.value.trim().toLowerCase(); return props.rows.filter((row) => { if (statusFilter.value !== "all" && row.statusKey !== statusFilter.value) return false; return !needle || [row.category, row.check, row.status, row.owner, row.detail, row.next, evidenceList(row).join(" ")].join(" ").toLowerCase().includes(needle); }); });
const pageRows = computed(() => filteredRows.value.slice((page.value - 1) * pageSize, page.value * pageSize));
const statusItems = computed(() => [
  { id: "status", label: "Status", value: props.payload?.status || "unknown", tone: statusCounts.value.failed || statusCounts.value.blocked ? "warn" as const : "ok" as const },
  { id: "rows", label: "Rows", value: props.rows.length, detail: props.payload?.contractVersion || "-" },
  { id: "blocked", label: "Blocked", value: statusCounts.value.blocked + statusCounts.value.failed, detail: `failed ${statusCounts.value.failed} / blocked ${statusCounts.value.blocked}` },
  { id: "observed", label: "Observed", value: displayDate(props.payload?.observedAt), detail: `liveBackend=${props.payload?.liveBackend ? 'true' : 'false'}` }
]);
function rowKey(row: GateDiagnosticRow): string { return `${row.category ?? "category"}:${row.check ?? "check"}`; }
function evidenceList(row: GateDiagnosticRow): string[] { if (Array.isArray(row.evidence)) return row.evidence.map(String); if (typeof row.evidence === "string" && row.evidence.trim()) return [row.evidence]; return []; }
function statusLabel(row: GateDiagnosticRow): string { return row.status ?? row.statusKey ?? "unknown"; }
function displayDate(value?: string): string { if (!value) return "-"; const date = new Date(value); return Number.isNaN(date.getTime()) ? value : date.toLocaleString("zh-CN", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" }); }
</script>

<template>
  <div class="gate-workspace" :data-inspector-open="Boolean(selectedRow)">
    <StatusStrip :items="statusItems" label="Gate live aggregation" live="polite" />
    <SectionFrame title="Diagnostics" description="live aggregation / filter / failure drill-down" :body-padding="false">
      <template #actions><button class="table-action" type="button" :disabled="loading" title="刷新 Gate diagnostics" @click="emit('refresh')"><RefreshCw :size="16" aria-hidden="true" />刷新</button></template>
      <div class="gate-filter-bar">
        <label><span>状态</span><select v-model="statusFilter"><option value="all">全部</option><option value="failed">failed</option><option value="blocked">blocked</option><option value="pending">pending</option><option value="pass">pass</option></select></label>
        <label><span>搜索</span><input v-model="query" type="search" placeholder="类别 / 检查项 / blocker / trace" /></label>
      </div>
      <DataGrid :columns="columns" :rows="pageRows" :row-key="rowKey" :selected-id="selectedRow ? rowKey(selectedRow) : null" empty-text="当前筛选没有 Gate 诊断行。" @select="emit('select', $event)">
        <template #cell-category="{ row }"><strong>{{ (row as GateDiagnosticRow).category || '-' }}</strong></template>
        <template #cell-check="{ row }"><span>{{ (row as GateDiagnosticRow).check || '-' }}</span><small>{{ displayDate((row as GateDiagnosticRow).updatedAt) }}</small></template>
        <template #cell-status="{ row }"><span class="status-pill" :data-status="(row as GateDiagnosticRow).statusKey || 'pending'">{{ statusLabel(row as GateDiagnosticRow) }}</span></template>
        <template #cell-owner="{ row }"><code>{{ (row as GateDiagnosticRow).owner || '-' }}</code></template>
        <template #cell-detail="{ row }"><span>{{ (row as GateDiagnosticRow).detail || '-' }}</span></template>
        <template #cell-actions="{ row }"><button class="icon-button" type="button" title="查看诊断详情" aria-label="查看诊断详情" @click.stop="emit('select', row as GateDiagnosticRow)"><Info :size="16" aria-hidden="true" /></button></template>
      </DataGrid>
      <Pagination v-model:page="page" :page-size="pageSize" :total="filteredRows.length" />
    </SectionFrame>

    <InspectorPanel v-if="selectedRow" title="Gate failure drill-down" :subtitle="selectedRow.check || ''" @close="emit('close')">
      <dl class="industrial-fact-row gate-facts"><div><dt>category</dt><dd>{{ selectedRow.category || '-' }}</dd></div><div><dt>status</dt><dd>{{ statusLabel(selectedRow) }}</dd></div><div><dt>owner</dt><dd>{{ selectedRow.owner || '-' }}</dd></div><div><dt>updated</dt><dd>{{ displayDate(selectedRow.updatedAt) }}</dd></div></dl>
      <SectionFrame title="关键细节" tone="inset"><p>{{ selectedRow.detail || '-' }}</p></SectionFrame>
      <SectionFrame title="证据 / trace" tone="inset"><ul><li v-for="item in evidenceList(selectedRow)" :key="item"><code>{{ item }}</code></li></ul></SectionFrame>
      <SectionFrame title="下一步" tone="inset"><p>{{ selectedRow.next || '-' }}</p></SectionFrame>
    </InspectorPanel>
  </div>
</template>

<style scoped>
.gate-workspace { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; min-width: 0; }
.gate-workspace[data-inspector-open="true"] { grid-template-columns: minmax(0, 1fr) minmax(300px, 380px); }
.gate-workspace > :first-child { grid-column: 1 / -1; }
.gate-filter-bar { display: grid; grid-template-columns: 180px minmax(260px, 1fr); gap: 8px; padding: 10px; }
.gate-filter-bar label { display: grid; gap: 4px; letter-spacing: 0; }
.gate-facts { grid-template-columns: 1fr 1fr; }
@media (max-width: 840px) { .gate-workspace[data-inspector-open="true"], .gate-filter-bar { grid-template-columns: 1fr; } }
</style>
