<!--
  组件名称：DeployScripts.vue
  组件描述：自动化部署测试 - 部署脚本配置（CRUD + 交互式应答编辑器）

  @author czj
  @date 2026-08-12
-->

<template>
  <div class="deploy-scripts-page">
    <div class="page-header">
      <h2>部署脚本</h2>
      <el-button type="primary" @click="openCreateDialog">
        <el-icon><Plus /></el-icon>新增脚本
      </el-button>
    </div>

    <el-card shadow="never">
      <el-table :data="scripts" v-loading="loading" stripe style="width: 100%">
        <el-table-column prop="name" label="脚本名称" min-width="150" />
        <el-table-column prop="serverName" label="目标服务器" min-width="140" />
        <el-table-column prop="remoteCommand" label="远程命令" min-width="200" show-overflow-tooltip />
        <el-table-column label="超时" width="80">
          <template #default="{ row }">
            {{ row.timeout }}s
          </template>
        </el-table-column>
        <el-table-column label="应答规则" width="100">
          <template #default="{ row }">
            <el-tag size="small">{{ row.expectAnswers?.length || 0 }} 条</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="更新时间" width="170">
          <template #default="{ row }">
            {{ formatTime(row.updatedAt) }}
          </template>
        </el-table-column>
        <el-table-column label="操作" width="160" fixed="right">
          <template #default="{ row }">
            <el-button link type="primary" size="small" @click="openEditDialog(row)">编辑</el-button>
            <el-popconfirm title="确定删除此脚本？" @confirm="handleDelete(row)">
              <template #reference>
                <el-button link type="danger" size="small">删除</el-button>
              </template>
            </el-popconfirm>
          </template>
        </el-table-column>
      </el-table>
    </el-card>

    <!-- 新增/编辑弹窗 -->
    <el-dialog v-model="dialogVisible" :title="isEdit ? '编辑部署脚本' : '新增部署脚本'" width="780px">
      <el-form ref="formRef" :model="form" :rules="rules" label-width="100px" label-position="top">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="脚本名称" prop="name">
              <el-input v-model="form.name" placeholder="如：部署统一平台" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="目标服务器" prop="serverId">
              <el-select v-model="form.serverId" placeholder="选择目标服务器" style="width: 100%">
                <el-option v-for="srv in serverOptions" :key="srv.id" :label="srv.name" :value="srv.id" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="远程命令" prop="remoteCommand">
          <el-input v-model="form.remoteCommand" placeholder="如：bash /opt/deploy/deploy.sh" />
        </el-form-item>
        <el-row :gutter="20">
          <el-col :span="16">
            <el-form-item label="工作目录">
              <el-input v-model="form.workDir" placeholder="留空使用默认目录（可选）" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="超时时间(秒)">
              <el-input-number v-model="form.timeout" :min="30" :max="86400" :step="60" style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="描述">
          <el-input v-model="form.description" type="textarea" :rows="2" placeholder="脚本说明（可选）" />
        </el-form-item>

        <el-divider content-position="left">交互式应答规则</el-divider>
        <div class="answer-editor">
          <div class="answer-editor-header">
            <span class="answer-editor-tip">配置脚本执行过程中可能出现的交互提示，系统将按顺序匹配并自动应答。</span>
            <el-button size="small" @click="addAnswerRule" :icon="Plus">添加规则</el-button>
          </div>
          <div v-if="form.expectAnswers.length === 0" class="answer-empty">
            暂无应答规则，脚本执行时将等待所有交互完成。
          </div>
          <div v-for="(rule, idx) in form.expectAnswers" :key="idx" class="answer-rule-row">
            <div class="answer-rule-header">
              <span class="rule-order">#{{ idx + 1 }}</span>
              <el-button type="danger" link size="small" @click="removeAnswerRule(idx)" :icon="Delete" />
            </div>
            <div class="answer-rule-body">
              <el-row :gutter="12">
                <el-col :span="8">
                  <el-form-item :label="`触发文本`" :prop="`expectAnswers.${idx}.trigger`" :rules="[{ required: true, message: '请输入触发文本', trigger: 'blur' }]">
                    <el-input v-model="rule.trigger" placeholder="如：password:" size="small" />
                  </el-form-item>
                </el-col>
                <el-col :span="4">
                  <el-form-item label="匹配方式">
                    <el-select v-model="rule.triggerType" size="small" style="width: 100%">
                      <el-option label="包含" value="contains" />
                      <el-option label="正则" value="regex" />
                    </el-select>
                  </el-form-item>
                </el-col>
                <el-col :span="6">
                  <el-form-item :label="`应答内容`" :prop="`expectAnswers.${idx}.answer`" :rules="[{ required: true, message: '请输入应答内容', trigger: 'blur' }]">
                    <el-input v-model="rule.answer" :type="rule.sensitive ? 'password' : 'text'" :show-password="rule.sensitive" placeholder="应答内容" size="small" />
                  </el-form-item>
                </el-col>
                <el-col :span="3">
                  <el-form-item label="敏感">
                    <el-switch v-model="rule.sensitive" size="small" />
                  </el-form-item>
                </el-col>
                <el-col :span="3">
                  <el-form-item label="超时(s)">
                    <el-input-number v-model="rule.timeout" :min="1" :max="300" size="small" style="width: 100%" />
                  </el-form-item>
                </el-col>
              </el-row>
              <el-form-item label="描述">
                <el-input v-model="rule.description" placeholder="此规则用途说明（可选）" size="small" />
              </el-form-item>
            </div>
          </div>
        </div>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" :loading="saving" @click="handleSave">保存</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
/**
 * 部署脚本配置：管理远程部署脚本及交互式应答规则
 */

import { ref, onMounted, reactive } from 'vue'
import { ElMessage } from 'element-plus'
import { Plus, Delete } from '@element-plus/icons-vue'
import {
  listServers,
  listScripts,
  createScript,
  updateScript,
  deleteScript,
} from '@/api/deploy'
import type { DeployServer, DeployScript, DeployScriptCreate, ExpectAnswer } from '@/types/deploy'

const loading = ref(false)
const scripts = ref<DeployScript[]>([])
const serverOptions = ref<DeployServer[]>([])
const dialogVisible = ref(false)
const isEdit = ref(false)
const saving = ref(false)
const editId = ref('')
const formRef = ref()

const defaultForm = (): Required<Pick<DeployScriptCreate, 'name' | 'description' | 'serverId' | 'remoteCommand' | 'workDir' | 'timeout' | 'expectAnswers'>> => ({
  name: '',
  description: '',
  serverId: '',
  remoteCommand: '',
  workDir: '',
  timeout: 3600,
  expectAnswers: [],
})

const form = reactive<Required<Pick<DeployScriptCreate, 'name' | 'description' | 'serverId' | 'remoteCommand' | 'workDir' | 'timeout' | 'expectAnswers'>>>(defaultForm())

const rules = {
  name: [{ required: true, message: '请输入脚本名称', trigger: 'blur' }],
  serverId: [{ required: true, message: '请选择目标服务器', trigger: 'change' }],
  remoteCommand: [{ required: true, message: '请输入远程命令', trigger: 'blur' }],
}

/** 加载脚本列表 */
async function loadScripts() {
  loading.value = true
  try {
    const res = await listScripts({ limit: 100 })
    scripts.value = res.items
  } catch (e: any) {
    ElMessage.error('加载脚本列表失败: ' + (e.message || ''))
  } finally {
    loading.value = false
  }
}

/** 加载服务器选项 */
async function loadServerOptions() {
  try {
    const res = await listServers({ limit: 200 })
    serverOptions.value = res.items
  } catch (_e) {
    // 非关键错误
  }
}

/** 打开新增弹窗 */
function openCreateDialog() {
  isEdit.value = false
  editId.value = ''
  Object.assign(form, defaultForm())
  dialogVisible.value = true
}

/** 打开编辑弹窗 */
function openEditDialog(row: DeployScript) {
  isEdit.value = true
  editId.value = row.id
  Object.assign(form, {
    name: row.name,
    description: row.description || '',
    serverId: row.serverId,
    remoteCommand: row.remoteCommand,
    workDir: row.workDir || '',
    timeout: row.timeout,
    expectAnswers: row.expectAnswers?.length ? row.expectAnswers.map((r: ExpectAnswer) => ({
      trigger: r.trigger,
      triggerType: r.triggerType || 'contains',
      answer: r.answer,
      timeout: r.timeout ?? 30,
      order: r.order ?? 0,
      sensitive: r.sensitive ?? false,
      description: r.description || '',
    })) : [],
  })
  dialogVisible.value = true
}

/** 添加应答规则 */
function addAnswerRule() {
  form.expectAnswers.push({
    trigger: '',
    triggerType: 'contains',
    answer: '',
    timeout: 30,
    order: form.expectAnswers.length,
    sensitive: false,
    description: '',
  })
}

/** 删除应答规则 */
function removeAnswerRule(index: number) {
  form.expectAnswers.splice(index, 1)
}

/** 保存脚本 */
async function handleSave() {
  const valid = await formRef.value?.validate().catch(() => false)
  if (!valid) return

  // 设置 order 字段
  form.expectAnswers.forEach((r, idx) => {
    r.order = idx
  })

  saving.value = true
  try {
    if (isEdit.value) {
      await updateScript(editId.value, form)
      ElMessage.success('部署脚本已更新')
    } else {
      await createScript(form)
      ElMessage.success('部署脚本已创建')
    }
    dialogVisible.value = false
    await loadScripts()
  } catch (e: any) {
    ElMessage.error('保存失败: ' + (e.message || ''))
  } finally {
    saving.value = false
  }
}

/** 删除脚本 */
async function handleDelete(row: DeployScript) {
  try {
    await deleteScript(row.id)
    ElMessage.success('部署脚本已删除')
    await loadScripts()
  } catch (e: any) {
    ElMessage.error('删除失败: ' + (e.message || ''))
  }
}

/** 格式化时间 */
function formatTime(t: string) {
  return t?.replace('T', ' ').substring(0, 19) || '-'
}

onMounted(() => {
  loadScripts()
  loadServerOptions()
})
</script>

<style scoped>
.deploy-scripts-page {
  padding: 0;
}

.page-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 16px;
}

.page-header h2 {
  font-size: 20px;
  font-weight: 600;
  color: #303133;
  margin: 0;
}

.answer-editor {
  border: 1px solid #e4e7ed;
  border-radius: 4px;
  padding: 12px;
}

.answer-editor-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;
}

.answer-editor-tip {
  font-size: 12px;
  color: #909399;
}

.answer-empty {
  text-align: center;
  padding: 24px;
  color: #909399;
  font-size: 13px;
}

.answer-rule-row {
  border: 1px solid #e4e7ed;
  border-radius: 4px;
  margin-bottom: 8px;
  padding: 8px;
  background: #fafafa;
}

.answer-rule-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}

.rule-order {
  font-weight: 600;
  font-size: 13px;
  color: #409eff;
}

.answer-rule-body {
  padding-left: 4px;
}
</style>