<!--
  页面名称：Cases.vue
  页面描述：用例管理页面，支持用例列表展示、筛选、编辑、删除、复制、导入导出

  @author czj
  @date 2026-07-09
-->

<template>
  <div class="cases-page">
    <!-- 页面头部 -->
    <div class="page-header">
      <div class="header-left">
        <el-tree-select
          v-model="filterModuleId"
          :data="moduleTreeData"
          :props="{ label: 'name', value: 'id', children: 'children' }"
          placeholder="输入关键字搜索模块"
          clearable
          check-strictly
          filterable
          style="width: 220px"
          @change="loadCases"
        />
        <el-input
          v-model="keyword"
          placeholder="搜索用例名称"
          clearable
          style="width: 200px"
          @input="loadCases"
        >
          <template #prefix>
            <el-icon><Search /></el-icon>
          </template>
        </el-input>
        <el-select
          v-model="filterStatus"
          placeholder="状态"
          clearable
          style="width: 120px"
          @change="loadCases"
        >
          <el-option label="启用" value="active" />
          <el-option label="禁用" value="disabled" />
        </el-select>
        <el-select
          v-model="filterPriority"
          placeholder="优先级"
          clearable
          style="width: 120px"
          @change="loadCases"
        >
          <el-option label="高" value="high" />
          <el-option label="中" value="medium" />
          <el-option label="低" value="low" />
        </el-select>
      </div>
      <div class="header-right">
        <el-button type="primary" @click="showCreateDialog">
          <el-icon><Plus /></el-icon>
          新建用例
        </el-button>
        <el-button @click="showImportDialog">
          <el-icon><Upload /></el-icon>
          导入
        </el-button>
        <el-button
          @click="exportCases"
          :disabled="selectedCases.length === 0"
        >
          <el-icon><Download /></el-icon>
          导出
        </el-button>
        <el-button
          type="primary"
          :disabled="selectedCases.length === 0"
          @click="runSelectedCases"
        >
          <el-icon><VideoPlay /></el-icon>
          执行选中 ({{ selectedCases.length }})
        </el-button>
      </div>
    </div>

    <!-- 用例列表 -->
    <el-card class="cases-table" shadow="never">
      <el-table
        :data="cases"
        stripe
        style="width: 100%"
        v-loading="loading"
        @selection-change="handleSelectionChange"
        :row-class-name="getRowClass"
      >
        <el-table-column type="selection" width="50" />
        <el-table-column label="用例名称" min-width="200">
          <template #default="{ row }">
            <div class="case-name-cell">
              <el-icon :size="18" class="case-icon"><Document /></el-icon>
              <span v-if="row.caseType === 'security'" class="security-level-emoji">
                {{ getSecurityLevelEmoji(row.steps?.vulnerability?.level) }}
              </span>
              <span>{{ row.name }}</span>
            </div>
          </template>
        </el-table-column>
        <el-table-column label="模块" width="150">
          <template #default="{ row }">
            <el-tag size="small" effect="plain">
              {{ getModuleName(row.module_id) }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="优先级" width="80">
          <template #default="{ row }">
            <el-tag
              :type="getPriorityType(row.priority)"
              size="small"
              effect="light"
            >
              {{ getPriorityText(row.priority) }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="状态" width="80">
          <template #default="{ row }">
            <el-tag
              :type="row.status === 'active' ? 'success' : 'info'"
              size="small"
              effect="plain"
            >
              {{ row.status === 'active' ? '启用' : '禁用' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="步骤数" width="80" align="center">
          <template #default="{ row }">
            <template v-if="row.caseType === 'security'">
              <el-tag
                :type="getSecurityLevelType(row.steps?.vulnerability?.level)"
                size="small"
                effect="light"
              >
                {{ getSecurityLevelText(row.steps?.vulnerability?.level) }}
              </el-tag>
            </template>
            <template v-else>
              <span class="step-count">{{ row.steps?.length || 0 }}</span>
            </template>
          </template>
        </el-table-column>
        <el-table-column label="标签" min-width="150">
          <template #default="{ row }">
            <div class="tags-cell">
              <el-tag
                v-for="tag in (row.tags || []).slice(0, 3)"
                :key="tag"
                size="small"
                type="info"
                effect="plain"
              >
                {{ tag }}
              </el-tag>
              <el-tag
                v-if="(row.tags || []).length > 3"
                size="small"
                type="info"
                effect="plain"
              >
                +{{ (row.tags || []).length - 3 }}
              </el-tag>
            </div>
          </template>
        </el-table-column>
        <el-table-column label="创建时间" width="180">
          <template #default="{ row }">
            {{ formatTime(row.created_at) }}
          </template>
        </el-table-column>
        <el-table-column label="操作" width="280" fixed="right">
          <template #default="{ row }">
            <el-button size="small" type="primary" link @click="showEditDialog(row)">
              编辑
            </el-button>
            <el-button size="small" type="primary" link @click="viewCaseDetail(row)">
              查看
            </el-button>
            <el-button
              v-if="row.caseType === 'ui'"
              size="small"
              type="success"
              link
              @click="showLocateConfig(row)"
              :loading="locatingCaseId === row.id"
            >
              智能定位
            </el-button>
            <el-button size="small" type="warning" link @click="copyCase(row)">
              复制
            </el-button>
            <el-button size="small" type="danger" link @click="deleteCase(row)">
              删除
            </el-button>
          </template>
        </el-table-column>
      </el-table>

      <div class="pagination">
        <el-pagination
          v-model:current-page="currentPage"
          v-model:page-size="pageSize"
          :total="total"
          layout="total, prev, pager, next, sizes"
          :page-sizes="[10, 20, 50, 100]"
          @current-change="loadCases"
          @size-change="loadCases"
        />
      </div>
    </el-card>

    <!-- 智能定位配置弹窗 -->
    <el-dialog
      v-model="locateConfigVisible"
      title="智能定位"
      width="500px"
      destroy-on-close
    >
      <el-form label-width="120px">
        <el-form-item label="用例名称">
          <el-input :value="locateConfigCaseName" disabled />
        </el-form-item>
        <el-alert
          type="info"
          :closable="false"
          style="margin-bottom: 16px"
        >
          智能定位将自动访问被测系统、执行操作并提取精确选择器，无需 Claude CLI。
        </el-alert>
        <el-divider content-position="left">配置</el-divider>
        <el-form-item label="目标菜单">
          <el-select
            v-model="locateConfig.navigate_menu"
            placeholder="选择目标菜单（可选）"
            style="width: 300px"
            clearable
          >
            <el-option
              v-for="opt in menuOptions"
              :key="opt.value"
              :label="opt.label"
              :value="opt.value"
            />
          </el-select>
          <div style="margin-top: 5px; color: #999; font-size: 12px">
            留空则根据步骤描述自动推断
          </div>
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="previewPage">预览页面</el-button>
        <el-button @click="locateConfigVisible = false">取消</el-button>
        <el-button type="primary" @click="batchLocate" :loading="locatingCaseId !== ''">
          开始定位
        </el-button>
      </template>
    </el-dialog>

    <!-- 创建/编辑用例弹窗 -->
    <el-dialog
      v-model="editDialogVisible"
      :title="editMode === 'create' ? '新建用例' : '编辑用例'"
      :width="editForm.case_type === 'ui' ? '700px' : '700px'"
      destroy-on-close
    >
      <!-- UI自动化用例：精简弹窗 -->
      <template v-if="editForm.case_type === 'ui'">
        <el-form
          ref="editFormRef"
          :model="editForm"
          :rules="editRules"
          label-width="100px"
        >
          <el-form-item label="用例名称" prop="name">
            <el-input v-model="editForm.name" placeholder="请输入用例名称" />
          </el-form-item>
          <el-form-item label="所属模块" prop="module_id">
            <el-tree-select
              v-model="editForm.module_id"
              :data="moduleTreeData"
              :props="{ label: 'name', value: 'id', children: 'children' }"
              placeholder="输入关键字搜索或选择模块"
              check-strictly
              filterable
              clearable
              style="width: 100%"
            />
          </el-form-item>
          <el-form-item label="测试步骤">
            <IntentStepEditor v-model="editForm.intentSteps" />
          </el-form-item>
        </el-form>
      </template>

      <!-- 非UI类型用例：保留原有完整弹窗 -->
      <template v-else>
        <el-form
          ref="editFormRef"
          :model="editForm"
          :rules="editRules"
          label-width="100px"
        >
          <el-form-item label="用例名称" prop="name">
            <el-input v-model="editForm.name" placeholder="请输入用例名称" />
          </el-form-item>
          <el-form-item label="所属模块" prop="module_id">
            <el-tree-select
              v-model="editForm.module_id"
              :data="moduleTreeData"
              :props="{ label: 'name', value: 'id', children: 'children' }"
              placeholder="输入关键字搜索或选择模块"
              check-strictly
              filterable
              clearable
              style="width: 100%"
            />
          </el-form-item>
          <el-form-item label="用例类型">
            <el-select v-model="editForm.case_type" style="width: 100%">
              <el-option
                v-for="opt in CASE_TYPE_OPTIONS"
                :key="opt.value"
                :label="opt.label"
                :value="opt.value"
              />
            </el-select>
          </el-form-item>
          <el-form-item label="优先级">
            <el-select v-model="editForm.priority" style="width: 100%">
              <el-option label="高" value="high" />
              <el-option label="中" value="medium" />
              <el-option label="低" value="low" />
            </el-select>
          </el-form-item>
          <el-form-item label="状态">
            <el-radio-group v-model="editForm.status">
              <el-radio value="active">启用</el-radio>
              <el-radio value="disabled">禁用</el-radio>
            </el-radio-group>
          </el-form-item>
          <el-form-item label="描述">
            <el-input
              v-model="editForm.description"
              type="textarea"
              :rows="3"
              placeholder="请输入用例描述"
            />
          </el-form-item>
          <el-form-item label="标签">
            <el-select
              v-model="editForm.tags"
              multiple
              filterable
              allow-create
              default-first-option
              placeholder="输入标签"
              style="width: 100%"
            >
              <el-option
                v-for="tag in commonTags"
                :key="tag"
                :label="tag"
                :value="tag"
              />
            </el-select>
          </el-form-item>
          <el-form-item label="测试步骤" v-if="editForm.case_type === 'ui'">
            <CaseStepEditor v-model="editForm.steps" />
          </el-form-item>
        </el-form>
      </template>

      <template #footer>
        <el-button @click="editDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="saveCase" :loading="saving">
          保存
        </el-button>
      </template>
    </el-dialog>

    <!-- 用例详情弹窗 -->
    <el-dialog
      v-model="detailDialogVisible"
      title="用例详情"
      width="800px"
      destroy-on-close
    >
      <div class="case-detail" v-if="currentCase">
        <div class="detail-header">
          <div class="detail-info">
            <h3>{{ currentCase.name }}</h3>
            <div class="detail-meta">
              <el-tag size="small">{{ getModuleName(currentCase.module_id) }}</el-tag>
              <el-tag :type="getPriorityType(currentCase.priority)" size="small">
                {{ getPriorityText(currentCase.priority) }}
              </el-tag>
              <el-tag :type="currentCase.status === 'active' ? 'success' : 'info'" size="small">
                {{ currentCase.status === 'active' ? '启用' : '禁用' }}
              </el-tag>
            </div>
            <p class="detail-desc" v-if="currentCase.description">
              {{ currentCase.description }}
            </p>
          </div>
        </div>

        <!-- 步骤列表（UI用例） -->
        <div class="steps-section" v-if="currentCase.caseType !== 'security'">
          <h4>测试步骤 ({{ currentCase.steps?.length || 0 }})</h4>
          <el-table
            :data="currentCase.steps || []"
            size="small"
            stripe
            v-if="currentCase.steps && currentCase.steps.length > 0"
          >
            <el-table-column label="#" width="50" type="index" />
            <el-table-column label="步骤名称" prop="name" min-width="120" />
            <el-table-column label="动作" width="90">
              <template #default="{ row }">
                <el-tag size="small" effect="plain">{{ row.action }}</el-tag>
              </template>
            </el-table-column>
            <el-table-column label="定位类型" width="100">
              <template #default="{ row }">
                <el-tag
                  v-if="row.locatorType || row.locator_type"
                  size="small"
                  type="success"
                >
                  {{ row.locatorType || row.locator_type }}
                </el-tag>
                <span v-else class="text-gray">-</span>
              </template>
            </el-table-column>
            <el-table-column label="定位值" min-width="180">
              <template #default="{ row }">
                <code
                  v-if="row.locatorValue || row.locator_value || row.params?.selector"
                  class="locator-code"
                >
                  {{ row.locatorValue || row.locator_value || row.params?.selector }}
                </code>
                <span v-else class="text-gray">-</span>
              </template>
            </el-table-column>
            <el-table-column label="参数" min-width="150">
              <template #default="{ row }">
                <span class="params-text">{{ formatParams(row.params) }}</span>
              </template>
            </el-table-column>
          </el-table>
          <el-empty v-else description="暂无步骤" :image-size="60" />
        </div>

        <!-- 安全测试详情 -->
        <div class="steps-section" v-if="currentCase.caseType === 'security' && currentCase.steps">
          <h4>安全测试配置</h4>
          <el-descriptions :column="2" border size="small">
            <el-descriptions-item label="测试类型">
              <el-tag size="small" effect="plain">{{ currentCase.steps.test_type || '-' }}</el-tag>
            </el-descriptions-item>
            <el-descriptions-item label="风险等级">
              <el-tag
                :type="getSecurityLevelType(currentCase.steps.vulnerability?.level)"
                size="small"
                effect="light"
              >
                {{ getSecurityLevelEmoji(currentCase.steps.vulnerability?.level) }}
                {{ getSecurityLevelText(currentCase.steps.vulnerability?.level) }}
              </el-tag>
            </el-descriptions-item>
            <el-descriptions-item label="请求方法" v-if="currentCase.steps.target">
              {{ currentCase.steps.target?.method }} {{ currentCase.steps.target?.path }}
            </el-descriptions-item>
            <el-descriptions-item label="测试账号" v-if="currentCase.steps.auth">
              {{ currentCase.steps.auth?.account || '-' }}
            </el-descriptions-item>
          </el-descriptions>

          <div v-if="currentCase.steps.vulnerability" style="margin-top: 12px">
            <h4>漏洞信息</h4>
            <el-descriptions :column="1" border size="small">
              <el-descriptions-item label="漏洞编号">
                {{ currentCase.steps.vulnerability.id || '-' }}
              </el-descriptions-item>
              <el-descriptions-item label="漏洞描述">
                {{ currentCase.steps.vulnerability.description || '-' }}
              </el-descriptions-item>
              <el-descriptions-item label="修复建议">
                {{ currentCase.steps.vulnerability.fix_suggestion || '-' }}
              </el-descriptions-item>
            </el-descriptions>
          </div>

          <div v-if="currentCase.steps.assertions?.length" style="margin-top: 12px">
            <h4>断言规则 ({{ currentCase.steps.assertions.length }})</h4>
            <el-table :data="currentCase.steps.assertions" size="small" stripe>
              <el-table-column label="#" width="50" type="index" />
              <el-table-column label="类型" prop="type" width="180" />
              <el-descriptions-item label="预期值" prop="expect" width="100" />
              <el-table-column label="说明" prop="description" min-width="200" />
            </el-table>
          </div>
        </div>
      </div>
    </el-dialog>

    <!-- 导入弹窗 -->
    <el-dialog
      v-model="importDialogVisible"
      title="导入用例"
      width="500px"
      destroy-on-close
    >
      <div class="import-content">
        <p class="import-tip">
          支持 JSON 格式的用例导入。请粘贴或上传 JSON 内容。
        </p>
        <el-input
          v-model="importJson"
          type="textarea"
          :rows="10"
          placeholder="请粘贴 JSON 格式的用例数据"
        />
        <div class="import-actions">
          <el-upload
            :auto-upload="false"
            accept=".json"
            :on-change="handleFileUpload"
            :show-file-list="false"
          >
            <el-button type="primary" link>
              <el-icon><Upload /></el-icon>
              上传 JSON 文件
            </el-button>
          </el-upload>
        </div>
      </div>
      <template #footer>
        <el-button @click="importDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="importCases" :loading="importing">
          导入
        </el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
/**
 * 用例管理页面
 *
 * 功能：
 * 1. 用例列表展示（分页、筛选）
 * 2. 创建、编辑用例
 * 3. 删除用例（带确认）
 * 4. 复制用例
 * 5. 导入导出用例
 * 6. 查看用例详情
 * 7. 执行选中用例
 */

import { ref, reactive, computed, watch, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage, ElMessageBox, FormInstance } from 'element-plus'
import {
  Search, Plus, Upload, Download, VideoPlay, Document
} from '@element-plus/icons-vue'
import { caseApi } from '@/api/cases'
import { moduleApi, buildModuleTree } from '@/api/modules'
import { executionApi } from '@/api/executions'
import { elementLocateApi } from '@/api/elementLocate'
import CaseStepEditor from '@/components/CaseStepEditor.vue'
import IntentStepEditor from '@/components/IntentStepEditor.vue'
import { toStepDefinition, toIntentStep } from '@/utils/stepParser'
import type { IntentStep } from '@/utils/stepParser'
import { usePageUrl } from '@/utils/usePageUrl'

const router = useRouter()
const route = useRoute()

// 设置目标网页 URL（用于 AI 定位）
const { setPageUrl } = usePageUrl()
const DEFAULT_TARGET_URL = 'https://192.168.5.44'
setPageUrl(DEFAULT_TARGET_URL)

// ==================== 用例类型配置 ====================

/** 用例类型选项 */
const CASE_TYPE_OPTIONS = [
  { label: 'UI自动化', value: 'ui' },
  { label: '安全自动化测试', value: 'security' },
]

/** 当前用例类型（来自路由参数，默认 ui） */
const currentType = computed(() => (route.params.type as string) || 'ui')

// ==================== 响应式数据 ====================

const loading = ref(false)
const cases = ref<any[]>([])
const total = ref(0)
const currentPage = ref(1)
const pageSize = ref(20)

// 筛选条件
const filterModuleId = ref('')
const keyword = ref('')
const filterStatus = ref('')
const filterPriority = ref('')
const selectedCases = ref<any[]>([])

// 模块列表
const modules = ref<any[]>([])

// 模块树形数据（用于 el-tree-select）
const moduleTreeData = computed(() => buildModuleTree(modules.value))

// 编辑弹窗
const editDialogVisible = ref(false)
const editMode = ref<'create' | 'edit'>('create')
const editFormRef = ref<FormInstance>()
const saving = ref(false)
const editForm = reactive({
  id: '',
  name: '',
  module_id: '',
  description: '',
  priority: 'medium',
  status: 'active',
  tags: [] as string[],
  case_type: 'ui',
  steps: [] as any[],
  intentSteps: [] as IntentStep[],
})

const editRules = {
  name: [{ required: true, message: '请输入用例名称', trigger: 'blur' }],
  module_id: [{ required: true, message: '请选择所属模块', trigger: 'change' }],
}

// 详情弹窗
const detailDialogVisible = ref(false)
const currentCase = ref<any>(null)

// 导入弹窗
const importDialogVisible = ref(false)
const importJson = ref('')
const importing = ref(false)

// 常用标签
const commonTags = ['登录', '核心流程', '回归测试', '冒烟测试', '边界测试', '异常测试']

// ==================== 方法定义 ====================

const loadModules = async () => {
  try {
    // 只加载 UI 测试模块，过滤掉安全测试模块
    const response = await moduleApi.list(0, 100, undefined, 'ui')
    modules.value = response.items
  } catch (error: any) {
    console.error('加载模块列表失败:', error)
  }
}

const loadCases = async () => {
  loading.value = true
  try {
    const response = await caseApi.list({
      module_id: filterModuleId.value,
      keyword: keyword.value,
      status: filterStatus.value,
      priority: filterPriority.value,
      case_type: currentType.value,
      skip: (currentPage.value - 1) * pageSize.value,
      limit: pageSize.value,
    })
    cases.value = response.items
    total.value = response.total
  } catch (error: any) {
    ElMessage.error('加载用例列表失败: ' + error.message)
  } finally {
    loading.value = false
  }
}

const handleSelectionChange = (selection: any[]) => {
  selectedCases.value = selection
}

const getModuleName = (moduleId: string) => {
  const module = modules.value.find(m => m.id === moduleId)
  return module?.name || moduleId
}

const getPriorityType = (priority: string) => {
  const map: Record<string, string> = {
    high: 'danger',
    medium: 'warning',
    low: 'info',
  }
  return map[priority] || 'info'
}

const getPriorityText = (priority: string) => {
  const map: Record<string, string> = {
    high: '高',
    medium: '中',
    low: '低',
  }
  return map[priority] || priority
}

/** 安全测试风险等级 emoji */
const getSecurityLevelEmoji = (level?: string) => {
  const map: Record<string, string> = {
    critical: '🔴🔴🔴',
    high: '🔴',
    medium: '🟠',
    low: '🟡',
    info: '🔵',
  }
  return map[level || ''] || '⚪'
}

/** 安全测试风险等级 Tag 类型 */
const getSecurityLevelType = (level?: string) => {
  const map: Record<string, string> = {
    critical: 'danger',
    high: 'danger',
    medium: 'warning',
    low: 'info',
    info: 'info',
  }
  return map[level || ''] || 'info'
}

/** 安全测试风险等级文本 */
const getSecurityLevelText = (level?: string) => {
  const map: Record<string, string> = {
    critical: '严重',
    high: '高危',
    medium: '中危',
    low: '低危',
    info: '信息',
  }
  return map[level || ''] || '-'
}

const formatTime = (time: string) => {
  if (!time) return '-'
  return new Date(time).toLocaleString('zh-CN', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
  })
}

const formatParams = (params: any) => {
  if (!params) return '-'
  if (typeof params === 'string') return params
  try {
    // 排除 selector（已在定位值列单独显示）
    const { selector, ...rest } = params
    const keys = Object.keys(rest)
    if (keys.length === 0) return '-'
    return JSON.stringify(rest)
  } catch {
    return '-'
  }
}

const getRowClass = ({ row }: { row: any }) => {
  if (row.status === 'disabled') return 'row-disabled'
  return ''
}

const showCreateDialog = () => {
  editMode.value = 'create'
  editForm.id = ''
  editForm.name = ''
  editForm.module_id = filterModuleId.value || ''
  editForm.description = ''
  editForm.priority = 'medium'
  editForm.status = 'active'
  editForm.tags = []
  editForm.case_type = 'ui'
  editForm.steps = []
  // UI 用例：初始化意图步骤
  editForm.intentSteps = [{
    order: 1,
    description: '',
    expected: '',
    actual: '',
    waitAfter: 0,
    locatorType: '',
    locatorValue: '',
  }]
  editDialogVisible.value = true
}

const showEditDialog = (row: any) => {
  editMode.value = 'edit'
  editForm.id = row.id
  editForm.name = row.name
  editForm.module_id = row.moduleId || row.module_id
  editForm.description = row.description || ''
  editForm.priority = row.priority || 'medium'
  editForm.status = row.status || 'active'
  editForm.tags = row.tags || []
  editForm.case_type = row.caseType || row.case_type || currentType.value
  editForm.steps = (row.steps || []).map((s: any) => ({ ...s }))

  if (editForm.case_type === 'ui') {
    // UI 用例：将现有步骤转换为意图步骤格式
    const rawSteps = row.steps || []
    editForm.intentSteps = rawSteps.length > 0
      ? rawSteps.map((s: any) => toIntentStep(s))
      : [{ order: 1, description: '', expected: '', actual: '', waitAfter: 0, locatorType: '', locatorValue: '' }]
  }

  editDialogVisible.value = true
}

const saveCase = async () => {
  if (!editFormRef.value) return

  try {
    await editFormRef.value.validate()
  } catch {
    return
  }

  saving.value = true
  try {
    // UI 用例：将意图步骤转换为 StepDefinition 格式
    if (editForm.case_type === 'ui') {
      editForm.steps = editForm.intentSteps
        .filter((s: IntentStep) => s.description.trim())
        .map((s: IntentStep) => toStepDefinition(s))
    }

    if (editMode.value === 'create') {
      await caseApi.create({
        name: editForm.name,
        module_id: editForm.module_id,
        description: editForm.description,
        priority: editForm.priority,
        status: editForm.status,
        tags: editForm.tags,
        case_type: editForm.case_type,
        steps: editForm.steps,
      })
      ElMessage.success('用例创建成功')
    } else {
      await caseApi.update(editForm.id, {
        name: editForm.name,
        module_id: editForm.module_id,
        description: editForm.description,
        priority: editForm.priority,
        status: editForm.status,
        tags: editForm.tags,
        case_type: editForm.case_type,
        steps: editForm.steps,
      })
      ElMessage.success('用例更新成功')
    }
    editDialogVisible.value = false
    await loadCases()
  } catch (error: any) {
    ElMessage.error('保存失败: ' + error.message)
  } finally {
    saving.value = false
  }
}

const viewCaseDetail = async (row: any) => {
  try {
    const detail = await caseApi.get(row.id)
    currentCase.value = detail
    detailDialogVisible.value = true
  } catch (error: any) {
    ElMessage.error('获取用例详情失败: ' + error.message)
  }
}

const copyCase = async (row: any) => {
  try {
    await ElMessageBox.confirm(`确定要复制用例 "${row.name}" 吗？`, '提示', {
      type: 'info',
    })
    await caseApi.copy(row.id)
    ElMessage.success('用例复制成功')
    await loadCases()
  } catch (error: any) {
    if (error !== 'cancel') {
      ElMessage.error('复制失败: ' + error.message)
    }
  }
}

const deleteCase = async (row: any) => {
  try {
    await ElMessageBox.confirm(
      `确定要删除用例 "${row.name}" 吗？删除后无法恢复。`,
      '删除确认',
      { type: 'warning' }
    )
    await caseApi.delete(row.id)
    ElMessage.success('用例删除成功')
    await loadCases()
  } catch (error: any) {
    if (error !== 'cancel') {
      ElMessage.error('删除失败: ' + error.message)
    }
  }
}

// ==================== 智能定位 ====================

/** 当前正在智能定位的用例 ID */
const locatingCaseId = ref('')

/** 配置面板相关状态 */
const locateConfigVisible = ref(false)
const locateConfigCaseId = ref('')
const locateConfigCaseName = ref('')
const locateConfig = ref({
  navigate_menu: '',  // 目标菜单名称
})

/** 可选的目标菜单列表 */
const menuOptions = [
  { label: '自动推断（推荐）', value: '' },
  { label: '会议管理', value: '会议管理' },
  { label: '信息管理', value: '信息管理' },
  { label: '数据统计', value: '数据统计' },
  { label: '运维管理', value: '运维管理' },
  { label: '资产管理', value: '资产管理' },
  { label: '会务管理', value: '会务管理' },
  { label: '维护工单', value: '维护工单' },
  { label: '集控控制', value: '集控控制' },
  { label: '信息发布', value: '信息发布' },
  { label: '系统设置', value: '系统设置' },
  { label: '会议运维', value: '会议运维' },
]

/**
 * 显示智能定位配置面板
 */
const showLocateConfig = (row: any) => {
  locateConfigCaseId.value = row.id
  locateConfigCaseName.value = row.name
  locateConfigVisible.value = true
}

/**
 * 预览页面（在新窗口打开已登录的目标页面）
 */
const previewPage = () => {
  window.open('https://192.168.5.44/', '_blank')
}

/**
 * 一键获取用例所有步骤的元素定位（智能定位）
 *
 * 流程：
 * 1. 获取用例详情（含步骤数据）
 * 2. 将步骤转换为智能定位请求格式
 * 3. 调用智能定位 API（实际执行 + 提取选择器）
 * 4. 将定位结果更新到用例步骤
 * 5. 前端显示结果
 */
const batchLocate = async () => {
  locateConfigVisible.value = false
  locatingCaseId.value = locateConfigCaseId.value

  try {
    ElMessage.info('正在启动浏览器智能定位，请稍候...')

    // 1. 获取用例详情
    const caseDetail = await caseApi.get(locateConfigCaseId.value)
    const steps = caseDetail.steps || []

    if (!steps.length) {
      ElMessage.warning('该用例没有步骤，请先添加步骤')
      locatingCaseId.value = ''
      return
    }

    // 2. 转换步骤为智能定位格式（包含 navigate 步骤）
    const smartLocateSteps = steps
      .filter((s: any) => s.action)
      .map((s: any) => ({
        order: s.order,
        name: s.name || '',
        action: s.action,
        params: s.params || {},
      }))

    if (!smartLocateSteps.length) {
      ElMessage.warning('没有可定位的步骤')
      locatingCaseId.value = ''
      return
    }

    // 3. 调用智能定位 API
    const result = await elementLocateApi.smartLocate({
      steps: smartLocateSteps,
      auto_login: true,
      navigate_menu: locateConfig.value.navigate_menu || undefined,
      page_url: 'https://192.168.5.44/',
    })

    if (result.success && result.located_steps > 0) {
      // 4. 将定位结果更新到用例步骤
      const updatedSteps = steps.map((s: any) => {
        // 查找对应的定位结果
        const locateResult = result.results.find(
          (r: any) => r.order === s.order && r.success
        )
        if (locateResult) {
          const updatedParams = {
            ...(s.params || {}),
            ...(locateResult.params || {}),
          }
          // 如果有 selector，更新选择器
          if (locateResult.selectors?.primary) {
            updatedParams.selector = locateResult.selectors.primary
          }
          return {
            ...s,
            params: updatedParams,
            locator_type: 'css',
            locator_value: locateResult.selectors?.primary || '',
          }
        }
        return s
      })

      // 5. 更新用例到数据库
      await caseApi.update(locateConfigCaseId.value, {
        steps: updatedSteps,
      })

      let msg = `智能定位完成：成功 ${result.located_steps}/${result.total_steps} 个步骤`
      ElMessage.success(msg)

      // 刷新用例列表
      await loadCases()
    } else {
      ElMessage.warning(result.message || '智能定位失败，未找到匹配元素')
    }
  } catch (error: any) {
    ElMessage.error('智能定位失败: ' + (error.message || '未知错误'))
  } finally {
    locatingCaseId.value = ''
  }
}

const runSelectedCases = async () => {
  if (selectedCases.value.length === 0) return

  try {
    const caseIds = selectedCases.value.map(c => c.id)
    const execution = await executionApi.create({
      case_ids: caseIds,
      name: `手动执行-${new Date().toLocaleDateString()}`,
    })
    // 创建成功后立即跳转到执行中心，不等待 run 完成
    // run 由执行中心页面自动触发（通过 query 参数传递 execution_id）
    ElMessage.success('执行任务已创建，正在跳转...')
    router.push({
      path: `/execution/${currentType.value}`,
      query: { run: execution.id },
    })
  } catch (error: any) {
    ElMessage.error('启动执行失败: ' + error.message)
  }
}

const showImportDialog = () => {
  importJson.value = ''
  importDialogVisible.value = true
}

const handleFileUpload = (file: any) => {
  const reader = new FileReader()
  reader.onload = (e) => {
    importJson.value = e.target?.result as string || ''
  }
  reader.readAsText(file.raw)
}

const importCases = async () => {
  if (!importJson.value.trim()) {
    ElMessage.warning('请输入或上传 JSON 内容')
    return
  }

  try {
    const data = JSON.parse(importJson.value)
    importing.value = true
    const result = await caseApi.import(Array.isArray(data) ? data : [data])
    ElMessage.success(`导入完成: 成功 ${result.success_count}，失败 ${result.failed_count}`)
    if (result.errors.length > 0) {
      console.warn('导入错误:', result.errors)
    }
    importDialogVisible.value = false
    await loadCases()
  } catch (error: any) {
    if (error instanceof SyntaxError) {
      ElMessage.error('JSON 格式错误，请检查内容')
    } else {
      ElMessage.error('导入失败: ' + (error.message || '未知错误'))
    }
  } finally {
    importing.value = false
  }
}

const exportCases = async () => {
  if (selectedCases.value.length === 0) {
    ElMessage.warning('请先选择要导出的用例')
    return
  }

  try {
    const caseIds = selectedCases.value.map(c => c.id)
    const data = await caseApi.export(caseIds)

    // 创建下载
    const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' })
    const url = URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = `cases_export_${new Date().toISOString().slice(0, 10)}.json`
    a.click()
    URL.revokeObjectURL(url)

    ElMessage.success(`已导出 ${data.length} 个用例`)
  } catch (error: any) {
    ElMessage.error('导出失败: ' + error.message)
  }
}

// ==================== 生命周期 ====================

// 路由类型变化时重新加载（切换用例类型子菜单）
watch(currentType, () => {
  currentPage.value = 1
  loadCases()
})

onMounted(() => {
  loadModules()
  loadCases()
})
</script>

<style lang="scss" scoped>
.cases-page {
  .page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
    flex-wrap: wrap;
    gap: 12px;

    .header-left {
      display: flex;
      gap: 12px;
    }

    .header-right {
      display: flex;
      gap: 12px;
    }
  }

  .cases-table {
    .case-name-cell {
      display: flex;
      align-items: center;
      gap: 8px;

      .case-icon {
        color: #409eff;
      }
    }

    .step-count {
      font-weight: 600;
      color: #606266;
    }

    .tags-cell {
      display: flex;
      gap: 4px;
      flex-wrap: wrap;
    }

    .row-disabled {
      background: #f5f5f5;
      opacity: 0.7;
    }

    .pagination {
      margin-top: 20px;
      display: flex;
      justify-content: flex-end;
    }
  }

  .case-detail {
    .detail-header {
      margin-bottom: 20px;

      .detail-info {
        h3 {
          margin: 0 0 12px 0;
          font-size: 20px;
        }

        .detail-meta {
          display: flex;
          gap: 8px;
          margin-bottom: 12px;
        }

        .detail-desc {
          color: #606266;
          font-size: 14px;
          margin: 0;
        }
      }
    }

    .steps-section {
      h4 {
        margin: 0 0 12px 0;
        font-size: 16px;
        color: #303133;
      }

      .params-text {
        font-size: 12px;
        color: #909399;
        word-break: break-all;
      }

      .locator-code {
        font-family: 'Consolas', 'Monaco', monospace;
        font-size: 11px;
        background: #f5f7fa;
        padding: 2px 6px;
        border-radius: 3px;
        color: #409eff;
        display: inline-block;
        max-width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
      }
    }
  }

  .import-content {
    .import-tip {
      margin-bottom: 12px;
      color: #909399;
      font-size: 14px;
    }

    .import-actions {
      margin-top: 12px;
    }
  }
}
</style>