<!--
  组件名称：IntentStepEditor.vue
  组件描述：意图步骤编辑器，支持自然语言步骤描述输入 + 定位获取

  功能：
  1. 添加 / 删除 / 上移 / 下移步骤
  2. 每行包含：步骤描述 + 等待时间 + 预期结果 + 定位信息
  3. "获取定位"按钮触发 AI 定位（调用后端 + 轮询结果）
  4. v-model 双向绑定 IntentStep 数组

  说明：
  - 专为 UI 自动化用例设计，用自然语言描述步骤意图
  - 保存时由父组件调用 stepParser 转换为 StepDefinition 格式
  - 定位信息独立存储（locatorType / locatorValue），与 params.selector 并存

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

<template>
  <div class="intent-step-editor">
    <!-- 步骤为空时的占位 -->
    <el-empty
      v-if="!steps.length"
      description="暂无步骤，点击下方按钮添加"
      :image-size="60"
    />

    <!-- 步骤列表 -->
    <div
      v-for="(step, index) in steps"
      :key="index"
      class="step-item"
    >
      <!-- 步骤头部：序号 + 操作按钮 -->
      <div class="step-header">
        <span class="step-order">{{ step.order }}</span>
        <span class="step-title">步骤{{ step.order }}</span>
        <div class="step-ops">
          <el-button
            size="small"
            link
            :disabled="index === 0"
            @click="moveStep(index, -1)"
            title="上移"
          >
            <el-icon><Top /></el-icon>
          </el-button>
          <el-button
            size="small"
            link
            :disabled="index === steps.length - 1"
            @click="moveStep(index, 1)"
            title="下移"
          >
            <el-icon><Bottom /></el-icon>
          </el-button>
          <el-button
            size="small"
            type="danger"
            link
            @click="removeStep(index)"
            title="删除"
          >
            <el-icon><Delete /></el-icon>
          </el-button>
        </div>
      </div>

      <!-- 步骤描述 + 等待时间（同一行） -->
      <div class="step-row">
        <span class="field-label">步骤描述</span>
        <el-input
          v-model="step.description"
          placeholder="如「输入用户名」「点击登录按钮」"
          size="small"
          style="flex: 1"
        />
        <span class="wait-label">等待</span>
        <el-input-number
          v-model="step.waitAfter"
          :min="0"
          :step="100"
          :max="30000"
          size="small"
          controls-position="right"
          style="width: 130px"
        />
        <span class="wait-label">ms</span>
      </div>

      <!-- 预期结果 -->
      <div class="step-row">
        <span class="field-label">预期结果</span>
        <el-input
          v-model="step.expected"
          placeholder="该步骤的预期结果（可选）"
          size="small"
          style="flex: 1"
        />
      </div>

      <!-- 实际结果（预留，编辑时回显） -->
      <div v-if="step.actual" class="step-row">
        <span class="field-label">实际结果</span>
        <el-input
          v-model="step.actual"
          placeholder="实际结果"
          size="small"
          disabled
          style="flex: 1"
        />
      </div>

      <!-- 定位信息区域 -->
      <div class="step-row locator-row">
        <el-button
          size="small"
          type="primary"
          :loading="locatingIndex === index"
          :disabled="!step.description.trim()"
          @click="handleLocate(index)"
        >
          <el-icon><Search /></el-icon>
          获取定位
        </el-button>
        <el-select
          v-model="step.locatorType"
          size="small"
          style="width: 120px"
          placeholder="定位类型"
          clearable
        >
          <el-option label="CSS 选择器" value="css" />
          <el-option label="XPath" value="xpath" />
          <el-option label="文本匹配" value="text" />
          <el-option label="ID 属性" value="id" />
          <el-option label="Name 属性" value="name" />
        </el-select>
        <el-input
          v-model="step.locatorValue"
          size="small"
          placeholder="定位值，如 #username"
          style="flex: 1"
          clearable
        />
      </div>
    </div>

    <!-- 添加步骤按钮 -->
    <el-button
      type="primary"
      plain
      size="small"
      class="add-step-btn"
      @click="addStep"
    >
      <el-icon><Plus /></el-icon>
      添加步骤
    </el-button>
  </div>
</template>

<script setup lang="ts">
/**
 * IntentStepEditor 组件
 *
 * 意图步骤编辑器，通过 v-model 双向绑定 IntentStep 数组。
 * 每行包含：步骤描述 + 等待时间 + 预期结果 + 定位信息。
 */
import { ref, computed } from 'vue'
import { ElMessage } from 'element-plus'
import { Top, Bottom, Delete, Plus, Search } from '@element-plus/icons-vue'
import type { IntentStep } from '@/utils/stepParser'
import { aiLocateApi } from '@/api/aiLocate'
import { usePageUrl } from '@/utils/usePageUrl'

const props = defineProps<{
  /** v-model 绑定的意图步骤数组 */
  modelValue: IntentStep[]
}>()

const emit = defineEmits<{
  (e: 'update:modelValue', value: IntentStep[]): void
}>()

/** steps 计算属性 */
const steps = computed(() => props.modelValue)

/** 当前正在获取定位的步骤索引 */
const locatingIndex = ref(-1)

/** 目标网页 URL */
const { pageUrl } = usePageUrl()

// ==================== 步骤增删移动 ====================

/** 添加步骤（自动编号） */
const addStep = () => {
  steps.value.push({
    order: steps.value.length + 1,
    description: '',
    expected: '',
    actual: '',
    waitAfter: 0,
    locatorType: '',
    locatorValue: '',
  })
  emitChange()
}

/** 删除步骤（重排 order） */
const removeStep = (index: number) => {
  steps.value.splice(index, 1)
  renumber()
}

/** 移动步骤 */
const moveStep = (index: number, dir: number) => {
  const target = index + dir
  if (target < 0 || target >= steps.value.length) return
  const tmp = steps.value[index]
  steps.value[index] = steps.value[target]
  steps.value[target] = tmp
  renumber()
}

/** 重新编号 order */
const renumber = () => {
  steps.value.forEach((s, i) => {
    s.order = i + 1
  })
  emitChange()
}

/** 通知父组件变更 */
const emitChange = () => {
  emit('update:modelValue', steps.value)
}

// ==================== AI 定位 ====================

/**
 * 获取定位信息
 *
 * 步骤：
 * 1. 创建定位任务（后端写入任务文件）
 * 2. 后端 SSH 调用宿主机 Claude Code 执行定位脚本
 * 3. 轮询结果，自动填充到定位字段
 */
const handleLocate = async (index: number) => {
  const step = steps.value[index]

  if (!step.description.trim()) {
    ElMessage.warning('请先填写步骤描述')
    return
  }

  if (!pageUrl.value) {
    ElMessage.warning('请先设置目标网页 URL（在系统配置中）')
    return
  }

  locatingIndex.value = index
  try {
    // 1. 创建定位任务
    const task = await aiLocateApi.locate({
      step_description: step.description,
      page_url: pageUrl.value,
      expected: step.expected,
    })

    ElMessage.info(`定位任务已创建 (${task.task_id})，正在等待结果...`)

    // 2. 轮询结果（每 3 秒一次，最多 60 秒）
    let retries = 20
    while (retries > 0) {
      await new Promise(r => setTimeout(r, 3000))
      const result = await aiLocateApi.getResult(task.task_id)

      if (result.status === 'completed') {
        if (result.success && result.candidates.length > 0) {
          const best = result.candidates[0]
          step.locatorType = best.locator_type
          step.locatorValue = best.locator_value
          ElMessage.success(`定位成功 (置信度: ${Math.round(best.confidence * 100)}%)`)
        } else {
          ElMessage.warning(result.message || '未找到匹配元素，请手动输入定位信息')
        }
        emitChange()
        return
      }

      if (result.status === 'failed') {
        ElMessage.error('定位失败: ' + result.message)
        return
      }

      retries--
    }

    // 超时：检查是否有结果文件但状态未更新
    try {
      const result = await aiLocateApi.getResult(task.task_id)
      if (result.status === 'completed' && result.candidates?.length > 0) {
        const best = result.candidates[0]
        step.locatorType = best.locator_type
        step.locatorValue = best.locator_value
        ElMessage.success(`定位成功 (置信度: ${Math.round(best.confidence * 100)}%)`)
        emitChange()
        return
      }
    } catch {
      // 忽略
    }

    ElMessage.warning('定位超时，请确认宿主机上定位脚本已运行，或手动输入定位信息')
  } catch (error: any) {
    ElMessage.error('定位失败: ' + (error.message || '未知错误'))
  } finally {
    locatingIndex.value = -1
  }
}
</script>

<style lang="scss" scoped>
.intent-step-editor {
  width: 100%;

  .step-item {
    padding: 14px;
    margin-bottom: 12px;
    background: #fafbfc;
    border: 1px solid #ebeef5;
    border-radius: 6px;
    transition: box-shadow 0.2s;

    &:hover {
      box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
    }

    .step-header {
      display: flex;
      align-items: center;
      gap: 8px;
      margin-bottom: 10px;

      .step-order {
        flex-shrink: 0;
        width: 24px;
        height: 24px;
        line-height: 24px;
        text-align: center;
        background: #409eff;
        color: #fff;
        border-radius: 50%;
        font-size: 12px;
        font-weight: 600;
      }

      .step-title {
        font-size: 13px;
        font-weight: 500;
        color: #303133;
      }

      .step-ops {
        margin-left: auto;
        display: flex;
        align-items: center;
      }
    }

    .step-row {
      display: flex;
      align-items: center;
      gap: 10px;
      margin-bottom: 8px;

      &:last-child {
        margin-bottom: 0;
      }

      .field-label {
        flex-shrink: 0;
        width: 70px;
        font-size: 13px;
        color: #606266;
        text-align: right;
      }

      .wait-label {
        flex-shrink: 0;
        font-size: 12px;
        color: #909399;
      }
    }

    .locator-row {
      padding-top: 8px;
      border-top: 1px dashed #e8eaed;
    }
  }

  .add-step-btn {
    width: 100%;
    border-style: dashed;
  }
}
</style>