<template>
  <div v-loading="loading">
    <PageHeader title="系统运维" description="查看服务健康、模型运行、接口集成和平台事件">
      <el-button :icon="Refresh" :loading="loading" @click="load">刷新状态</el-button>
      <el-button @click="openPrometheus">Prometheus</el-button>
    </PageHeader>

    <section class="metric-strip">
      <div v-for="item in metrics" :key="item.label" class="metric-card">
        <strong>{{ item.value }}</strong><span>{{ item.label }}</span><small>{{ item.note }}</small>
      </div>
    </section>

    <el-tabs v-model="activeTab" class="secondary-tabs">
      <el-tab-pane label="服务状态" name="services">
        <section class="service-grid">
          <div v-for="service in services" :key="service.name" class="service-card">
            <header>
              <el-icon><component :is="service.icon" /></el-icon>
              <span><strong>{{ service.name }}</strong><small>{{ service.endpoint }}</small></span>
              <i class="status-dot" :class="{ danger: health.status !== 'UP' }"></i>
            </header>
            <dl><div><dt>状态</dt><dd>{{ health.status === 'UP' ? '运行正常' : '连接异常' }}</dd></div><div><dt>检查方式</dt><dd>{{ service.check }}</dd></div></dl>
          </div>
        </section>

        <section class="runtime-panel">
          <header>
            <div>
              <strong>模型运行时</strong>
              <span>{{ runtimeProfile }} · {{ artifactRows.length }} 个模型组已配置路由</span>
            </div>
            <div class="runtime-summary">
              <el-radio-group v-model="runtimeScope" size="small">
                <el-radio-button value="active">当前配置</el-radio-button>
                <el-radio-button value="all">全部环境</el-radio-button>
              </el-radio-group>
              <el-tag v-if="installerStatus !== 'ok'" type="danger" effect="plain">安装代理不可用</el-tag>
              <el-tag effect="plain" :type="readyCount === artifactRows.length ? 'success' : 'warning'">
                {{ readyCount }}/{{ artifactRows.length }} 个制品就绪
              </el-tag>
            </div>
          </header>

          <el-alert
            v-if="installerStatus !== 'ok'"
            :title="installerMessage"
            type="error"
            :closable="false"
            show-icon
          />

          <el-table :data="artifactRows" empty-text="暂未获取到模型运行配置" class="runtime-table">
            <el-table-column prop="model_group_name" label="模型配置" min-width="180">
              <template #default="scope">
                <div class="model-cell"><strong>{{ scope.row.model_group_name }}</strong><span>{{ scope.row.runtime_deployment_name }}</span></div>
              </template>
            </el-table-column>
            <el-table-column prop="model_group_code" label="模型组" min-width="170">
              <template #default="scope"><span class="entity-id">{{ scope.row.model_group_code }}</span></template>
            </el-table-column>
            <el-table-column prop="model_version" label="版本" width="120" />
            <el-table-column label="环境" width="100">
              <template #default="scope">{{ environmentText(scope.row.environment) }}</template>
            </el-table-column>
            <el-table-column label="制品状态" width="126">
              <template #default="scope">
                <el-tag :type="artifactStatusType(scope.row)" effect="plain">{{ artifactStatusText(scope.row) }}</el-tag>
              </template>
            </el-table-column>
            <el-table-column label="运行状态" width="116">
              <template #default="scope">
                <span class="load-state"><i :class="{ ready: scope.row.loaded }"></i>{{ loadStatusText(scope.row) }}</span>
              </template>
            </el-table-column>
            <el-table-column label="操作" width="330" fixed="right">
              <template #default="scope">
                <div class="action-row" v-if="scope.row.package_format !== 'builtin'">
                  <el-button size="small" :icon="UploadFilled" @click="openUpload(scope.row)">上传</el-button>
                  <el-button
                    size="small"
                    type="primary"
                    :icon="Download"
                    :disabled="!canInstall(scope.row)"
                    @click="openInstall(scope.row)"
                  >安装</el-button>
                  <el-button
                    v-if="scope.row.installation_id && !scope.row.loaded"
                    size="small"
                    :icon="VideoPlay"
                    :disabled="scope.row.installation_status !== 'ready' && scope.row.installation_status !== 'unloaded'"
                    @click="runRuntimeAction(scope.row, 'load')"
                  >加载</el-button>
                  <el-button
                    v-if="scope.row.loaded"
                    size="small"
                    :icon="SwitchButton"
                    @click="runRuntimeAction(scope.row, 'unload')"
                  >释放</el-button>
                  <el-button
                    v-if="scope.row.installation_id"
                    size="small"
                    type="danger"
                    plain
                    :icon="Delete"
                    @click="prepareUninstall(scope.row)"
                  >卸载</el-button>
                  <el-tooltip v-if="isJobActive(scope.row)" content="查看当前任务进度">
                    <el-button circle size="small" :icon="View" @click="trackJob(String(scope.row.job_id))" />
                  </el-tooltip>
                </div>
                <span v-else class="builtin-label">随运行环境内置</span>
              </template>
            </el-table-column>
          </el-table>

          <footer>
            <span>执行提供程序：{{ executionProvider }}</span>
            <span>视觉并发：{{ visionRuntime.max_concurrency || '-' }}</span>
            <span>空间并发：{{ spatialRuntime.max_concurrency || '-' }}</span>
            <span>制品库：{{ artifactStoreAvailable ? '可用' : '不可用' }}</span>
          </footer>
        </section>
      </el-tab-pane>

      <el-tab-pane label="接口集成" name="integrations">
        <el-card class="workspace-card" shadow="never"><el-table :data="integrations"><el-table-column prop="name" label="集成系统" min-width="150" /><el-table-column prop="type" label="方式" width="110" /><el-table-column prop="endpoint" label="地址或通道" min-width="220" /><el-table-column label="状态" width="110"><template #default><el-tag type="success" effect="plain">已连接</el-tag></template></el-table-column><el-table-column prop="description" label="用途" min-width="200" /></el-table></el-card>
      </el-tab-pane>
      <el-tab-pane label="组织与责任" name="organization">
        <div class="page-grid two-columns">
          <el-card class="workspace-card" shadow="never"><template #header><div class="card-heading"><strong>组织结构</strong><span>{{ organizationRows.length }} 个组织节点</span></div></template><el-table :data="organizationRows"><el-table-column prop="name" label="组织" min-width="150" /><el-table-column prop="org_type" label="类型" width="105" /><el-table-column prop="user_count" label="人员" width="70" /><el-table-column prop="responsibility_count" label="责任规则" width="90" /></el-table></el-card>
          <el-card class="workspace-card" shadow="never"><template #header><div class="card-heading"><strong>责任匹配规则</strong><span>线路与专业自动派单</span></div></template><el-table :data="responsibilityRows"><el-table-column prop="line_id" label="线路" width="100" /><el-table-column prop="object_type" label="对象" width="100" /><el-table-column prop="owner_org_name" label="责任单位" min-width="130" /><el-table-column prop="assignee_name" label="默认人员" min-width="110" /></el-table></el-card>
        </div>
      </el-tab-pane>
      <el-tab-pane :label="`消息与任务 ${eventRows.length}`" name="events">
        <el-card class="workspace-card" shadow="never"><el-table :data="eventRows" empty-text="暂无平台事件"><el-table-column prop="event_id" label="事件编号" min-width="170"><template #default="scope"><span class="entity-id">{{ scope.row.event_id }}</span></template></el-table-column><el-table-column prop="event_type" label="事件类型" min-width="190" /><el-table-column label="载荷" min-width="260"><template #default="scope">{{ payloadText(scope.row.payload) }}</template></el-table-column><el-table-column label="时间" min-width="170"><template #default="scope">{{ formatDateTime(scope.row.created_at) }}</template></el-table-column></el-table></el-card>
      </el-tab-pane>
      <el-tab-pane label="系统配置" name="config">
        <section class="config-grid"><div v-for="group in configGroups" :key="group.title"><header><strong>{{ group.title }}</strong><span>{{ group.description }}</span></header><el-descriptions :column="1" border size="small"><el-descriptions-item v-for="item in group.items" :key="item.label" :label="item.label">{{ item.value }}</el-descriptions-item></el-descriptions></div></section>
      </el-tab-pane>
      <el-tab-pane label="操作审计" name="audit">
        <el-alert title="业务操作均通过平台事件记录，可按事件类型、实体和时间追溯。" type="info" show-icon :closable="false" /><el-timeline class="audit-timeline"><el-timeline-item v-for="event in eventRows.slice(0, 30)" :key="event.event_id" :timestamp="formatDateTime(event.created_at)" placement="top"><strong>{{ event.event_type }}</strong><p>{{ payloadText(event.payload) }}</p></el-timeline-item></el-timeline>
      </el-tab-pane>
    </el-tabs>

    <el-dialog v-model="uploadDialog.visible" title="上传模型制品" width="620px" :close-on-click-modal="false">
      <div class="dialog-context"><strong>{{ uploadDialog.row.model_group_name }}</strong><span>{{ uploadDialog.row.model_version }} · {{ environmentText(uploadDialog.row.environment) }}</span></div>
      <el-form label-position="top">
        <el-form-item label="主模型文件（ONNX）" required>
          <el-upload :auto-upload="false" :limit="1" accept=".onnx" :on-change="onPrimaryChange" :on-remove="onPrimaryRemove"><el-button :icon="UploadFilled">选择主模型</el-button></el-upload>
        </el-form-item>
        <el-form-item v-if="uploadDialog.row.model_group_code === 'thermal-analyzer'" label="辅助模型文件（ONNX）" required>
          <el-upload :auto-upload="false" :limit="1" accept=".onnx" :on-change="onSecondaryChange" :on-remove="onSecondaryRemove"><el-button :icon="UploadFilled">选择辅助模型</el-button></el-upload>
        </el-form-item>
        <el-form-item label="标签文件（TXT，每行一个类别）" required>
          <el-upload :auto-upload="false" :limit="1" accept=".txt" :on-change="onLabelsChange" :on-remove="onLabelsRemove"><el-button :icon="UploadFilled">选择标签文件</el-button></el-upload>
        </el-form-item>
        <el-progress v-if="uploadDialog.uploading" :percentage="uploadDialog.progress" />
      </el-form>
      <template #footer><el-button @click="uploadDialog.visible = false">取消</el-button><el-button type="primary" :loading="uploadDialog.uploading" @click="submitUpload">上传并登记</el-button></template>
    </el-dialog>

    <el-dialog v-model="installDialog.visible" title="安装模型制品" width="560px" :close-on-click-modal="false">
      <el-alert title="安装代理将校验文件摘要和 ONNX 可加载性，再原子发布到运行目录。" type="info" :closable="false" show-icon />
      <el-descriptions :column="1" border class="install-summary">
        <el-descriptions-item label="模型">{{ installDialog.row.model_group_name }}</el-descriptions-item>
        <el-descriptions-item label="版本">{{ installDialog.row.model_version }}</el-descriptions-item>
        <el-descriptions-item label="目标环境">{{ environmentText(installDialog.row.environment) }}</el-descriptions-item>
        <el-descriptions-item label="组件数">{{ installDialog.row.artifact_file_count || 0 }}</el-descriptions-item>
      </el-descriptions>
      <el-form label-position="top">
        <el-form-item v-if="installDialog.row.environment === 'production'" label="审批单号" required><el-input v-model="installDialog.approvalTicket" placeholder="请输入已审批的变更单号" /></el-form-item>
        <el-form-item label="安装后加载"><el-switch v-model="installDialog.loadAfterInstall" /></el-form-item>
      </el-form>
      <template #footer><el-button @click="installDialog.visible = false">取消</el-button><el-button type="primary" :loading="installDialog.submitting" @click="submitInstall">确认安装</el-button></template>
    </el-dialog>

    <el-dialog v-model="jobDialog.visible" :title="jobTitle" width="680px" :close-on-click-modal="false" :close-on-press-escape="!jobRunning">
      <div class="job-head"><el-progress type="circle" :percentage="jobProgress" :status="jobProgressStatus" /><div><el-tag :type="jobStatusType" effect="plain">{{ jobStatusText }}</el-tag><strong>{{ currentJob.current_stage || 'queued' }}</strong><span>{{ currentJob.error_message || latestJobMessage }}</span></div></div>
      <el-timeline class="job-timeline">
        <el-timeline-item v-for="event in jobEvents" :key="String(event.id)" :timestamp="formatDateTime(event.created_at)" :type="event.status === 'failed' ? 'danger' : event.status === 'completed' ? 'success' : 'primary'">
          <strong>{{ event.message }}</strong><span>{{ Number(event.progress || 0) }}%</span>
        </el-timeline-item>
      </el-timeline>
      <template #footer><el-button :disabled="jobRunning" @click="closeJobDialog">关闭</el-button></template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, reactive, ref } from "vue";
import { Connection, Cpu, DataLine, Delete, Download, Refresh, SetUp, SwitchButton, UploadFilled, VideoPlay, View } from "@element-plus/icons-vue";
import { ElMessage, ElMessageBox, type UploadFile } from "element-plus";
import PageHeader from "../../components/common/PageHeader.vue";
import {
  aiRuntimeStatus,
  installModelArtifact,
  loadModelArtifact,
  modelArtifactJob,
  modelArtifactRuntimeOverview,
  modelArtifactUninstallImpact,
  organizations as loadOrganizations,
  overview,
  platformEvents,
  responsibilityRules,
  serviceHealth,
  uninstallModelArtifact,
  unloadModelArtifact,
  uploadModelArtifact
} from "../../services/api";
import { safeObject, type Row } from "../../types/demo-run";

const loading = ref(false);
const activeTab = ref("services");
const runtimeScope = ref<"active" | "all">("active");
const health = ref<Row>({ status: "UNKNOWN" });
const stats = ref<Row>({});
const eventRows = ref<Row[]>([]);
const organizationRows = ref<Row[]>([]);
const responsibilityRows = ref<Row[]>([]);
const runtime = ref<Row>({});
const artifactOverview = ref<Row>({});
const currentJob = ref<Row>({});
let jobTimer: number | undefined;

const uploadDialog = reactive<{ visible: boolean; row: Row; primary?: File; secondary?: File; labels?: File; uploading: boolean; progress: number }>({ visible: false, row: {}, uploading: false, progress: 0 });
const installDialog = reactive<{ visible: boolean; row: Row; approvalTicket: string; loadAfterInstall: boolean; submitting: boolean }>({ visible: false, row: {}, approvalTicket: "", loadAfterInstall: true, submitting: false });
const jobDialog = reactive({ visible: false });

const visionRuntime = computed(() => safeObject(runtime.value.vision));
const spatialRuntime = computed(() => safeObject(runtime.value.spatial));
const artifactRows = computed<Row[]>(() => {
  const rows = asRows(artifactOverview.value.deployments);
  return runtimeScope.value === "active" ? rows.filter(row => Boolean(row.active)) : rows;
});
const installerInfo = computed(() => safeObject(artifactOverview.value.installer));
const installerStatus = computed(() => String(installerInfo.value.status || "unavailable"));
const installerMessage = computed(() => String(installerInfo.value.message || "模型制品安装代理当前不可用，请检查 8103 服务。"));
const artifactStoreAvailable = computed(() => Boolean(artifactOverview.value.artifact_store_available));
const readyCount = computed(() => artifactRows.value.filter(row => row.package_format === "builtin" || row.installation_status === "ready").length);
const runtimeProfile = computed(() => String(installerInfo.value.profile || visionRuntime.value.profile || spatialRuntime.value.profile || "未配置"));
const executionProvider = computed(() => String(visionRuntime.value.execution_provider || spatialRuntime.value.execution_provider || "-"));
const metrics = computed(() => [
  { label: "平台状态", value: health.value.status === "UP" ? "正常" : "异常", note: "Actuator 健康检查" },
  { label: "模型制品", value: `${readyCount.value}/${artifactRows.value.length}`, note: runtimeProfile.value },
  { label: "业务对象", value: Number(stats.value.tasks || 0) + Number(stats.value.resources || 0), note: "任务与资源" },
  { label: "分析结果", value: stats.value.ai_results || 0, note: "累计推理结果" },
  { label: "处置工单", value: stats.value.workorders || 0, note: "业务闭环记录" }
]);
const jobEvents = computed<Row[]>(() => asRows(currentJob.value.events));
const jobProgress = computed(() => Math.round(Number(currentJob.value.progress || 0)));
const jobRunning = computed(() => ["queued", "running"].includes(String(currentJob.value.status)));
const jobProgressStatus = computed(() => currentJob.value.status === "failed" ? "exception" : currentJob.value.status === "completed" ? "success" : undefined);
const jobStatusType = computed(() => currentJob.value.status === "failed" ? "danger" : currentJob.value.status === "completed" ? "success" : "primary");
const jobStatusText = computed(() => ({ queued: "排队中", running: "执行中", completed: "已完成", failed: "失败" }[String(currentJob.value.status)] || "等待状态"));
const jobTitle = computed(() => ({ install: "模型制品安装", load: "模型加载", unload: "运行资源释放", uninstall: "模型制品卸载" }[String(currentJob.value.job_type)] || "模型制品任务"));
const latestJobMessage = computed(() => String(jobEvents.value[jobEvents.value.length - 1]?.message || "正在等待任务执行"));

const services = [{ name: "业务平台", endpoint: ":8080", check: "Actuator", icon: SetUp }, { name: "视觉推理服务", endpoint: ":8101", check: "HTTP Health", icon: Cpu }, { name: "点云分析服务", endpoint: ":8102", check: "HTTP Health", icon: DataLine }, { name: "模型安装代理", endpoint: ":8103", check: "HTTP Health", icon: Download }, { name: "PostGIS / Kafka / Redis / MinIO", endpoint: "基础设施", check: "容器健康检查", icon: Connection }];
const integrations = [{ name: "无人机任务接口", type: "SDK/API", endpoint: "/api/v1/uav/callbacks/mission-status", description: "航线任务和飞行状态回调" }, { name: "多源数据接入", type: "REST", endpoint: "/api/v1/inspection/resources/complete", description: "资源清单与接入完成通知" }, { name: "工单系统", type: "REST", endpoint: "/api/v1/workorders/callbacks/status", description: "工单处置状态同步" }, { name: "业务事件总线", type: "Kafka", endpoint: "platform-events", description: "任务、告警、工单和样本事件" }];
const configGroups = [{ title: "数据基础设施", description: "持久化、缓存、消息和对象存储", items: [{ label: "数据库", value: "PostgreSQL 16 + PostGIS 3.4" }, { label: "缓存", value: "Redis 7" }, { label: "消息", value: "Kafka 3.8.1" }, { label: "对象存储", value: "MinIO S3" }] }, { title: "算法服务", description: "视觉、空间分析与制品管理", items: [{ label: "视觉推理", value: "FastAPI :8101" }, { label: "点云分析", value: "FastAPI :8102" }, { label: "制品安装", value: "FastAPI :8103" }, { label: "模型交换", value: "ONNX Runtime / TensorRT 适配" }] }, { title: "平台与监控", description: "业务服务和可观测性", items: [{ label: "业务平台", value: "Spring Boot 3.3" }, { label: "前端", value: "Vue 3 + TypeScript" }, { label: "指标", value: "Prometheus :9090" }] }];

function openUpload(row: Row) {
  Object.assign(uploadDialog, { visible: true, row, primary: undefined, secondary: undefined, labels: undefined, uploading: false, progress: 0 });
}
function setUploadFile(type: "primary" | "secondary" | "labels", file: UploadFile) { if (file.raw) uploadDialog[type] = file.raw; }
function clearUploadFile(type: "primary" | "secondary" | "labels") { uploadDialog[type] = undefined; }
function onPrimaryChange(file: UploadFile) { setUploadFile("primary", file); }
function onSecondaryChange(file: UploadFile) { setUploadFile("secondary", file); }
function onLabelsChange(file: UploadFile) { setUploadFile("labels", file); }
function onPrimaryRemove() { clearUploadFile("primary"); }
function onSecondaryRemove() { clearUploadFile("secondary"); }
function onLabelsRemove() { clearUploadFile("labels"); }
async function submitUpload() {
  const thermal = uploadDialog.row.model_group_code === "thermal-analyzer";
  if (!uploadDialog.primary || !uploadDialog.labels || (thermal && !uploadDialog.secondary)) {
    ElMessage.warning("请补齐模型制品所需文件"); return;
  }
  uploadDialog.uploading = true;
  try {
    await uploadModelArtifact(String(uploadDialog.row.runtime_deployment_id), { primary: uploadDialog.primary, secondary: uploadDialog.secondary, labels: uploadDialog.labels }, value => uploadDialog.progress = value);
    ElMessage.success("模型制品已上传并登记"); uploadDialog.visible = false; await load();
  } catch (error) { ElMessage.error(errorMessage(error)); }
  finally { uploadDialog.uploading = false; }
}
function openInstall(row: Row) { Object.assign(installDialog, { visible: true, row, approvalTicket: "", loadAfterInstall: true, submitting: false }); }
async function submitInstall() {
  if (installDialog.row.environment === "production" && !installDialog.approvalTicket.trim()) { ElMessage.warning("生产环境必须填写审批单号"); return; }
  installDialog.submitting = true;
  try {
    const job = await installModelArtifact(String(installDialog.row.runtime_deployment_id), String(installDialog.row.artifact_id), installDialog.approvalTicket, installDialog.loadAfterInstall);
    installDialog.visible = false; trackJob(String(job.id));
  } catch (error) { ElMessage.error(errorMessage(error)); }
  finally { installDialog.submitting = false; }
}
async function runRuntimeAction(row: Row, action: "load" | "unload") {
  try {
    const job = action === "load" ? await loadModelArtifact(String(row.installation_id)) : await unloadModelArtifact(String(row.installation_id));
    trackJob(String(job.id));
  } catch (error) { ElMessage.error(errorMessage(error)); }
}
async function prepareUninstall(row: Row) {
  try {
    const impact = safeObject(await modelArtifactUninstallImpact(String(row.installation_id)));
    const blockers = asRows(impact.blockers);
    if (!impact.allowed) { await ElMessageBox.alert(blockers.map(item => item.message).join("；"), "当前不能卸载", { type: "warning" }); return; }
    const answer = await ElMessageBox.prompt("卸载会释放运行时会话并删除受管模型文件，请填写原因。", "确认卸载", { confirmButtonText: "卸载", cancelButtonText: "取消", inputPlaceholder: "请输入卸载原因", inputValidator: value => Boolean(value?.trim()) || "必须填写卸载原因", type: "warning" });
    const job = await uninstallModelArtifact(String(row.installation_id), answer.value);
    trackJob(String(job.id));
  } catch (error) { if (String((error as { action?: string })?.action) !== "cancel") ElMessage.error(errorMessage(error)); }
}
function trackJob(jobId: string) {
  window.clearTimeout(jobTimer); jobDialog.visible = true; currentJob.value = { id: jobId, status: "queued", progress: 0, events: [] }; pollJob(jobId);
}
async function pollJob(jobId: string) {
  try {
    currentJob.value = safeObject(await modelArtifactJob(jobId));
    if (["queued", "running"].includes(String(currentJob.value.status))) jobTimer = window.setTimeout(() => pollJob(jobId), 1000);
    else await load();
  } catch (error) { ElMessage.error(errorMessage(error)); }
}
function closeJobDialog() { jobDialog.visible = false; currentJob.value = {}; }
function canInstall(row: Row) { return Number(row.artifact_file_count || 0) > 0 && !isJobActive(row) && installerStatus.value === "ok"; }
function isJobActive(row: Row) { return ["queued", "running"].includes(String(row.job_status)); }
function artifactStatusText(row: Row) { if (row.package_format === "builtin") return "内置就绪"; if (isJobActive(row)) return "处理中"; if (row.installation_status === "ready" || row.installation_status === "unloaded") return "已安装"; if (row.installation_status === "install_failed") return "安装失败"; return Number(row.artifact_file_count || 0) > 0 ? "待安装" : "待上传"; }
function artifactStatusType(row: Row) { const status = artifactStatusText(row); if (["内置就绪", "已安装"].includes(status)) return "success"; if (status === "安装失败") return "danger"; return "warning"; }
function loadStatusText(row: Row) { if (row.package_format === "builtin") return "内置加载"; if (row.loaded) return "已加载"; if (row.installation_status === "unloaded") return "已释放"; return "未加载"; }
function environmentText(value: unknown) { return ({ local: "本地", development: "开发", staging: "预发布", production: "生产" }[String(value)] || String(value || "-")); }
function payloadText(value: unknown) { const data = safeObject(value); return Object.entries(data).slice(0, 4).map(([key, item]) => `${key}: ${item}`).join("，") || "-"; }
function asRows(value: unknown): Row[] { return Array.isArray(value) ? value.map(item => safeObject(item)) : []; }
function formatDateTime(value: unknown) { return value ? new Date(String(value)).toLocaleString("zh-CN", { hour12: false }) : "-"; }
function errorMessage(error: unknown) { const item = error as { response?: { data?: { message?: string; detail?: string; error?: string } }; message?: string }; return item.response?.data?.message || item.response?.data?.detail || item.response?.data?.error || item.message || "操作失败"; }
function openPrometheus() { window.open("http://localhost:9090", "_blank", "noopener,noreferrer"); }
async function load() {
  loading.value = true;
  try {
    const [healthData, statData, platformEventData, runtimeData, artifactData, organizationData, responsibilityData] = await Promise.all([serviceHealth(), overview(), platformEvents(), aiRuntimeStatus(), modelArtifactRuntimeOverview(), loadOrganizations(), responsibilityRules()]);
    health.value = healthData; stats.value = statData; eventRows.value = platformEventData; runtime.value = runtimeData; artifactOverview.value = artifactData; organizationRows.value = organizationData; responsibilityRows.value = responsibilityData;
  } catch (error) { ElMessage.error(errorMessage(error)); }
  finally { loading.value = false; }
}
onMounted(load);
onBeforeUnmount(() => window.clearTimeout(jobTimer));
</script>

<style scoped>
.runtime-panel { margin-top: 18px; border-top: 1px solid var(--el-border-color-light); padding-top: 16px; }
.runtime-panel > header, .runtime-panel > footer { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.runtime-panel > header { margin-bottom: 12px; }
.runtime-panel > header > div:first-child, .model-cell, .dialog-context { display: grid; gap: 3px; }
.runtime-panel > header strong { font-size: 15px; }
.runtime-panel > header span, .runtime-panel > footer, .model-cell span, .dialog-context span { color: var(--el-text-color-secondary); font-size: 12px; }
.runtime-summary, .action-row { display: flex; align-items: center; gap: 8px; }
.action-row { gap: 6px; white-space: nowrap; }
.action-row :deep(.el-button + .el-button) { margin-left: 0; }
.runtime-table { margin-top: 12px; }
.runtime-panel > footer { justify-content: flex-start; flex-wrap: wrap; margin-top: 10px; }
.load-state { display: inline-flex; align-items: center; gap: 6px; }
.load-state i { width: 7px; height: 7px; border-radius: 50%; background: var(--el-text-color-placeholder); }
.load-state i.ready { background: var(--el-color-success); box-shadow: 0 0 0 3px var(--el-color-success-light-8); }
.builtin-label { color: var(--el-text-color-secondary); font-size: 12px; }
.dialog-context { padding-bottom: 14px; margin-bottom: 16px; border-bottom: 1px solid var(--el-border-color-lighter); }
.install-summary { margin: 16px 0; }
.job-head { display: grid; grid-template-columns: 120px 1fr; align-items: center; gap: 24px; padding: 8px 12px 20px; border-bottom: 1px solid var(--el-border-color-lighter); }
.job-head > div { display: grid; justify-items: start; gap: 10px; }
.job-head strong { font-size: 16px; }
.job-head span { color: var(--el-text-color-secondary); line-height: 1.6; }
.job-timeline { margin-top: 24px; max-height: 330px; overflow: auto; padding-right: 12px; }
.job-timeline strong { display: inline-block; margin-right: 10px; }
.job-timeline span { color: var(--el-text-color-secondary); font-size: 12px; }
@media (max-width: 900px) { .runtime-panel > header { align-items: flex-start; flex-direction: column; } .action-row { flex-wrap: wrap; } }
</style>
