<template>
  <div class="runtime-deployment-panel" v-loading="loading">
    <section v-if="activeDeployment" class="active-runtime-band">
      <div class="active-runtime-state">
        <span class="runtime-live-dot"></span>
        <span><small>当前生效配置</small><strong>{{ activeDeployment.name }}</strong></span>
      </div>
      <dl>
        <div><dt>模型版本</dt><dd>{{ activeDeployment.model_version }}</dd></div>
        <div><dt>运行环境</dt><dd>{{ environmentLabel(activeDeployment.environment) }}</dd></div>
        <div><dt>运行引擎</dt><dd>{{ activeDeployment.runtime_engine }}</dd></div>
        <div><dt>执行设备</dt><dd>{{ activeDeployment.device }}</dd></div>
        <div><dt>服务连接</dt><dd><el-tag :type="connectivityTag(activeDeployment.connectivity_status)" effect="plain">{{ connectivityLabel(activeDeployment.connectivity_status) }}</el-tag></dd></div>
        <div><dt>运行就绪</dt><dd><el-tag :type="healthTag(activeDeployment.health_status)" effect="plain">{{ readinessLabel(activeDeployment) }}</el-tag></dd></div>
      </dl>
      <div class="active-runtime-actions">
        <el-button :icon="Connection" :loading="checkingConnectionId === activeDeployment.id" @click="checkConnection(activeDeployment)">检查连接</el-button>
        <el-button type="primary" plain :icon="CircleCheck" :loading="checkingReadinessId === activeDeployment.id" @click="checkReadiness(activeDeployment)">运行检查</el-button>
      </div>
    </section>

    <el-alert v-if="activeMissingArtifacts.length" class="runtime-readiness-alert" type="warning" :closable="false" show-icon>
      <template #title>服务连接正常，模型制品待安装</template>
      <p>{{ activeDeployment?.health_message }}</p>
      <div class="missing-artifact-list">
        <code v-for="path in activeMissingArtifacts" :key="path">{{ path }}<small>本地目录：{{ localArtifactPath(path) }}</small></code>
      </div>
      <p>完成制品安装并重启对应推理服务后，再执行“运行检查”。</p>
    </el-alert>

    <div class="model-toolbar runtime-toolbar">
      <span class="toolbar-note">运行配置决定实际推理端点、模型版本、执行提供程序和并发资源。切换前自动执行健康检查。</span>
      <span class="filter-spacer"></span>
      <el-button :icon="Refresh" @click="load">刷新</el-button>
      <el-button type="primary" :icon="Plus" @click="openCreate">新增配置</el-button>
    </div>

    <section class="runtime-deployment-table">
      <el-table :data="deployments" empty-text="暂无运行配置">
        <el-table-column label="配置" min-width="205">
          <template #default="scope">
            <div class="runtime-name-cell"><strong>{{ scope.row.name }}</strong><small>{{ scope.row.id }}</small></div>
          </template>
        </el-table-column>
        <el-table-column label="环境" width="105"><template #default="scope"><el-tag :type="environmentTag(scope.row.environment)" effect="plain">{{ environmentLabel(scope.row.environment) }}</el-tag></template></el-table-column>
        <el-table-column label="模型版本" min-width="125"><template #default="scope"><strong>{{ scope.row.model_version }}</strong><small class="cell-subtext">{{ scope.row.model_version_name }}</small></template></el-table-column>
        <el-table-column label="端点" min-width="205"><template #default="scope"><span class="runtime-endpoint">{{ scope.row.service_url }}</span><small class="cell-subtext">{{ scope.row.inference_path }}</small></template></el-table-column>
        <el-table-column label="运行资源" min-width="180"><template #default="scope">{{ scope.row.runtime_engine }}<small class="cell-subtext">{{ scope.row.execution_provider }} · {{ scope.row.precision }}</small></template></el-table-column>
        <el-table-column label="容量" width="115"><template #default="scope">{{ scope.row.replicas }} 副本<small class="cell-subtext">并发 {{ scope.row.max_concurrency }}</small></template></el-table-column>
        <el-table-column label="服务连接" width="120"><template #default="scope"><el-tag :type="connectivityTag(scope.row.connectivity_status)" effect="plain">{{ connectivityLabel(scope.row.connectivity_status) }}</el-tag><small class="cell-subtext">{{ scope.row.last_connectivity_at ? formatDateTime(scope.row.last_connectivity_at) : '尚未检查' }}</small></template></el-table-column>
        <el-table-column label="运行就绪" width="125"><template #default="scope"><el-tag :type="healthTag(scope.row.health_status)" effect="plain">{{ readinessLabel(scope.row) }}</el-tag><small class="cell-subtext">{{ scope.row.last_health_at ? formatDateTime(scope.row.last_health_at) : '尚未校验' }}</small></template></el-table-column>
        <el-table-column label="状态" width="95"><template #default="scope"><el-tag :type="scope.row.active ? 'success' : 'info'" :effect="scope.row.active ? 'dark' : 'plain'">{{ scope.row.active ? '生效中' : '待切换' }}</el-tag></template></el-table-column>
        <el-table-column label="操作" width="370" fixed="right">
          <template #default="scope">
            <div class="list-actions">
              <el-button link type="primary" :icon="Connection" :loading="checkingConnectionId === scope.row.id" @click="checkConnection(scope.row)">检查连接</el-button>
              <el-button link type="primary" :icon="CircleCheck" :loading="checkingReadinessId === scope.row.id" @click="checkReadiness(scope.row)">运行检查</el-button>
              <el-button link type="success" :icon="SwitchButton" :disabled="scope.row.active" :loading="switchingId === scope.row.id" @click="activate(scope.row)">切换</el-button>
              <el-dropdown trigger="click">
                <el-button link>更多<el-icon class="el-icon--right"><ArrowDown /></el-icon></el-button>
                <template #dropdown><el-dropdown-menu>
                  <el-dropdown-item :icon="Edit" :disabled="scope.row.active" @click="openEdit(scope.row)">编辑配置</el-dropdown-item>
                  <el-dropdown-item :icon="CopyDocument" @click="cloneDeployment(scope.row)">复制配置</el-dropdown-item>
                  <el-dropdown-item :icon="Delete" :disabled="scope.row.active" divided @click="remove(scope.row)">删除配置</el-dropdown-item>
                </el-dropdown-menu></template>
              </el-dropdown>
            </div>
          </template>
        </el-table-column>
      </el-table>
    </section>

    <el-drawer v-model="drawerVisible" :title="editingId ? '编辑运行配置' : '新增运行配置'" size="min(760px, 96vw)" destroy-on-close>
      <el-form label-position="top" class="runtime-config-form" @submit.prevent>
        <section class="runtime-form-section">
          <header><strong>基础信息</strong><span>关联平台中的模型版本和使用环境</span></header>
          <div class="runtime-form-grid">
            <el-form-item label="配置名称" required><el-input v-model="form.name" maxlength="128" placeholder="例如：服务器 GPU 生产" /></el-form-item>
            <el-form-item label="模型版本" required><el-select v-model="form.modelVersionId"><el-option v-for="version in versions" :key="version.id" :label="`${version.version} · ${version.display_name}`" :value="version.id" /></el-select></el-form-item>
            <el-form-item label="运行环境" required><el-segmented v-model="form.environment" :options="environmentOptions" /></el-form-item>
            <el-form-item label="模型制品地址"><el-input v-model="form.artifactUri" placeholder="file:///models/... 或 s3://bucket/..." /></el-form-item>
          </div>
        </section>

        <section class="runtime-form-section">
          <header><strong>服务接口</strong><span>端点需实现统一健康、运行时、推理和测试契约</span></header>
          <el-form-item label="服务根地址" required><el-input v-model="form.serviceUrl" placeholder="http://vision-inference:8101" /></el-form-item>
          <div class="runtime-path-grid">
            <el-form-item label="健康检查"><el-input v-model="form.healthPath" /></el-form-item>
            <el-form-item label="运行时清单"><el-input v-model="form.runtimePath" /></el-form-item>
            <el-form-item label="生产推理"><el-input v-model="form.inferencePath" /></el-form-item>
            <el-form-item label="模型测试"><el-input v-model="form.testPath" /></el-form-item>
          </div>
          <el-form-item label="认证密钥引用"><el-input v-model="form.authSecretRef" placeholder="Kubernetes Secret 或 Vault 引用，不填写明文密钥" /></el-form-item>
        </section>

        <section class="runtime-form-section">
          <header><strong>运行资源</strong><span>配置执行引擎、设备、精度和容量上限</span></header>
          <div class="runtime-form-grid runtime-resource-grid">
            <el-form-item label="运行引擎" required><el-select v-model="form.runtimeEngine" filterable allow-create><el-option v-for="item in runtimeOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item>
            <el-form-item label="执行提供程序" required><el-select v-model="form.executionProvider" filterable allow-create><el-option v-for="item in providerOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item>
            <el-form-item label="设备" required><el-input v-model="form.device" placeholder="cpu、cuda:0" /></el-form-item>
            <el-form-item label="精度" required><el-select v-model="form.precision"><el-option v-for="item in ['FP64', 'FP32', 'FP16', 'INT8']" :key="item" :label="item" :value="item" /></el-select></el-form-item>
            <el-form-item label="副本数"><el-input-number v-model="form.replicas" :min="1" :max="100" controls-position="right" /></el-form-item>
            <el-form-item label="最大并发"><el-input-number v-model="form.maxConcurrency" :min="1" :max="1000" controls-position="right" /></el-form-item>
            <el-form-item label="请求超时（秒）"><el-input-number v-model="form.requestTimeoutSeconds" :min="1" :max="3600" controls-position="right" /></el-form-item>
            <el-form-item label="推理批量"><el-input-number v-model="form.batchSize" :min="1" :max="128" controls-position="right" /></el-form-item>
          </div>
        </section>

        <section class="runtime-form-section">
          <header><strong>切换策略</strong><span>运行档案用于防止把本地端点误切为生产配置</span></header>
          <div class="runtime-policy-row">
            <el-form-item label="期望运行档案"><el-input v-model="form.expectedProfile" placeholder="cpu-local 或 server-gpu" /></el-form-item>
            <el-form-item label="允许基线降级"><el-switch v-model="form.allowFallback" /></el-form-item>
          </div>
        </section>
      </el-form>
      <template #footer><div class="drawer-actions"><el-button @click="drawerVisible = false">取消</el-button><el-button type="primary" :loading="saving" @click="save">保存配置</el-button></div></template>
    </el-drawer>
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, ref, watch } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { ArrowDown, CircleCheck, Connection, CopyDocument, Delete, Edit, Plus, Refresh, SwitchButton } from "@element-plus/icons-vue";
import {
  activateModelRuntimeDeployment, checkModelRuntimeConnectivity, checkModelRuntimeDeployment, createModelRuntimeDeployment,
  deleteModelRuntimeDeployment, modelRuntimeDeployments, updateModelRuntimeDeployment
} from "../../services/api";
import { formatDateTime, type ModelGroup, type ModelVersion, type RuntimeDeployment } from "../../types/model-management";

interface RuntimeForm {
  name: string; modelVersionId: string; environment: string; serviceUrl: string;
  healthPath: string; runtimePath: string; inferencePath: string; testPath: string;
  runtimeEngine: string; executionProvider: string; device: string; precision: string;
  artifactUri: string; authSecretRef: string; replicas: number; maxConcurrency: number;
  requestTimeoutSeconds: number; expectedProfile: string; allowFallback: boolean; batchSize: number;
}

const props = defineProps<{ group: ModelGroup; versions: ModelVersion[] }>();
const loading = ref(false); const saving = ref(false); const checkingConnectionId = ref("");
const checkingReadinessId = ref(""); const switchingId = ref("");
const deployments = ref<RuntimeDeployment[]>([]); const drawerVisible = ref(false); const editingId = ref("");
const environmentOptions = [{ label: "本地", value: "local" }, { label: "开发", value: "development" }, { label: "预发布", value: "staging" }, { label: "生产", value: "production" }];
const runtimeOptions = ["onnxruntime", "onnxruntime-gpu", "tensorrt", "paddle-inference", "open3d", "triton", "custom"];
const providerOptions = ["CPUExecutionProvider", "CUDAExecutionProvider", "TensorrtExecutionProvider", "OpenVINOExecutionProvider", "CPU"];
const activeDeployment = computed(() => deployments.value.find((item) => item.active));
const activeMissingArtifacts = computed(() => activeDeployment.value ? missingArtifacts(activeDeployment.value) : []);
const isSpatial = computed(() => ["pointcloud-analyzer", "change-detector"].includes(props.group.code));
const form = ref<RuntimeForm>(defaultForm());

function defaultForm(): RuntimeForm {
  const spatial = ["pointcloud-analyzer", "change-detector"].includes(props.group.code);
  return {
    name: "", modelVersionId: props.versions.find((item) => item.status === "production")?.id || props.versions[0]?.id || "",
    environment: "production", serviceUrl: spatial ? "http://pointcloud-analysis:8102" : "http://vision-inference:8101",
    healthPath: "/health", runtimePath: "/api/v1/runtime",
    inferencePath: spatial ? "/api/v1/pointcloud/analyze" : "/api/v1/inference/detect",
    testPath: spatial ? "/api/v1/pointcloud/test" : "/api/v1/inference/test",
    runtimeEngine: spatial && props.group.code === "pointcloud-analyzer" ? "open3d" : "onnxruntime-gpu",
    executionProvider: spatial && props.group.code === "pointcloud-analyzer" ? "CPU" : "CUDAExecutionProvider",
    device: spatial && props.group.code === "pointcloud-analyzer" ? "cpu:server-pool" : "cuda:0", precision: spatial && props.group.code === "pointcloud-analyzer" ? "FP64" : "FP16",
    artifactUri: "", authSecretRef: "", replicas: 2, maxConcurrency: spatial ? 4 : 8,
    requestTimeoutSeconds: spatial ? 180 : 60, expectedProfile: "server-gpu", allowFallback: false, batchSize: spatial ? 1 : 4
  };
}

async function load() { loading.value = true; try { deployments.value = await modelRuntimeDeployments(props.group.id); } finally { loading.value = false; } }
function openCreate() { editingId.value = ""; form.value = defaultForm(); drawerVisible.value = true; }
function openEdit(row: RuntimeDeployment) {
  const config = row.config || {};
  editingId.value = row.id;
  form.value = {
    name: row.name, modelVersionId: row.model_version_id, environment: row.environment, serviceUrl: row.service_url,
    healthPath: row.health_path, runtimePath: row.runtime_path, inferencePath: row.inference_path, testPath: row.test_path,
    runtimeEngine: row.runtime_engine, executionProvider: row.execution_provider, device: row.device, precision: row.precision,
    artifactUri: row.artifact_uri || "", authSecretRef: row.auth_secret_ref || "", replicas: Number(row.replicas),
    maxConcurrency: Number(row.max_concurrency), requestTimeoutSeconds: Number(row.request_timeout_seconds),
    expectedProfile: String(config.expected_profile || ""), allowFallback: Boolean(config.allow_fallback), batchSize: Number(config.batch_size || 1)
  };
  drawerVisible.value = true;
}
function cloneDeployment(row: RuntimeDeployment) { openEdit(row); editingId.value = ""; form.value.name = `${row.name} 副本`; }

async function save() {
  if (!form.value.name.trim() || !form.value.modelVersionId || !form.value.serviceUrl.trim()) return ElMessage.warning("请完整填写名称、模型版本和服务地址");
  saving.value = true;
  const payload = {
    model_version_id: form.value.modelVersionId, name: form.value.name.trim(), environment: form.value.environment,
    service_url: form.value.serviceUrl.trim(), health_path: form.value.healthPath, runtime_path: form.value.runtimePath,
    inference_path: form.value.inferencePath, test_path: form.value.testPath, runtime_engine: form.value.runtimeEngine,
    execution_provider: form.value.executionProvider, device: form.value.device, precision: form.value.precision,
    artifact_uri: form.value.artifactUri || null, auth_secret_ref: form.value.authSecretRef || null,
    replicas: form.value.replicas, max_concurrency: form.value.maxConcurrency,
    request_timeout_seconds: form.value.requestTimeoutSeconds,
    config: { expected_profile: form.value.expectedProfile || null, allow_fallback: form.value.allowFallback, batch_size: form.value.batchSize },
    operator: "模型运维管理员"
  };
  try {
    if (editingId.value) await updateModelRuntimeDeployment(editingId.value, payload); else await createModelRuntimeDeployment(props.group.id, payload);
    ElMessage.success("运行配置已保存"); drawerVisible.value = false; await load();
  } finally { saving.value = false; }
}

async function checkConnection(row: RuntimeDeployment) {
  checkingConnectionId.value = row.id;
  try {
    const result = await checkModelRuntimeConnectivity(row.id);
    if (result.connectivity_status === "connected") ElMessage.success(result.connectivity_message || "服务连接正常");
    else ElMessage.error(result.connectivity_message || "服务不可连接");
    await load();
  } finally { checkingConnectionId.value = ""; }
}
async function checkReadiness(row: RuntimeDeployment) {
  checkingReadinessId.value = row.id;
  try {
    const result = await checkModelRuntimeDeployment(row.id);
    if (result.health_status === "healthy") ElMessage.success(result.health_message || "模型运行环境已就绪");
    else ElMessage.warning(result.health_message || healthLabel(result.health_status));
    await load();
  } finally { checkingReadinessId.value = ""; }
}
async function activate(row: RuntimeDeployment) {
  await ElMessageBox.confirm(`将 ${props.group.name} 切换为“${row.name}”（${row.model_version}）。切换前会检查端点和模型制品。`, "确认切换运行配置", { type: "warning", confirmButtonText: "检查并切换", cancelButtonText: "取消" });
  switchingId.value = row.id;
  try { await activateModelRuntimeDeployment(row.id); ElMessage.success("运行配置已切换，后续任务立即使用新端点和版本"); await load(); }
  finally { switchingId.value = ""; }
}
async function remove(row: RuntimeDeployment) {
  await ElMessageBox.confirm(`删除运行配置“${row.name}”？`, "删除确认", { type: "warning" });
  await deleteModelRuntimeDeployment(row.id); ElMessage.success("运行配置已删除"); await load();
}

function environmentLabel(value: string) { return ({ local: "本地", development: "开发", staging: "预发布", production: "生产" } as Record<string, string>)[value] || value; }
function environmentTag(value: string): "success" | "warning" | "info" | "primary" { return value === "production" ? "success" : value === "staging" ? "warning" : value === "local" ? "info" : "primary"; }
function healthLabel(value: string) { return ({ healthy: "健康", degraded: "降级可用", unreachable: "不可连接", incompatible: "配置不匹配", unknown: "待检查" } as Record<string, string>)[value] || value; }
function healthTag(value: string): "success" | "warning" | "danger" | "info" { return value === "healthy" ? "success" : value === "degraded" ? "warning" : ["unreachable", "incompatible"].includes(value) ? "danger" : "info"; }
function connectivityLabel(value: string) { return ({ connected: "已连接", unreachable: "不可连接", unknown: "待检查" } as Record<string, string>)[value] || "待检查"; }
function connectivityTag(value: string): "success" | "danger" | "info" { return value === "connected" ? "success" : value === "unreachable" ? "danger" : "info"; }
function missingArtifacts(row: RuntimeDeployment): string[] {
  const value = row.health_snapshot?.missing_artifacts;
  return Array.isArray(value) ? value.map(String) : [];
}
function readinessLabel(row: RuntimeDeployment) {
  return row.health_status === "degraded" && missingArtifacts(row).length ? "制品待安装" : healthLabel(row.health_status);
}
function localArtifactPath(path: string) { return path.startsWith("/models/") ? `runtime/models/${path.slice(8)}` : path; }

watch(() => props.group.id, load);
onMounted(load);
</script>
