<!--
  组件名称：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>

      <!-- 状态验证折叠区（P1：意图模式支持 verify 编辑，防假通过） -->
      <div class="step-row verify-toggle-row">
        <el-button
          size="small"
          link
          type="primary"
          @click="toggleAdvanced(index)"
        >
          <el-icon class="toggle-icon" :class="{ expanded: !!advancedOpen[index] }"><ArrowRight /></el-icon>
          {{ hasVerify(step) ? `状态验证（${verifyTypeLabel(step)}）` : '状态验证（可选，防假通过）' }}
        </el-button>
      </div>
      <div v-if="advancedOpen[index]" class="verify-area">
        <div class="step-row">
          <span class="field-label">验证类型</span>
          <el-select
            v-model="getVerify(step).type"
            size="small"
            style="width: 200px"
            placeholder="选择验证类型"
            clearable
          >
            <el-option
              v-for="t in VERIFY_TYPE_OPTIONS"
              :key="t.value"
              :label="t.label"
              :value="t.value"
            />
          </el-select>
          <el-input
            v-if="verifyNeedsSelector(getVerify(step).type)"
            v-model="getVerify(step).selector"
            placeholder="验证选择器（CSS/XPath）"
            size="small"
            style="flex: 1"
          />
          <el-input
            v-if="verifyNeedsText(getVerify(step).type)"
            v-model="getVerify(step).text"
            placeholder="期望文本/URL片段"
            size="small"
            style="width: 240px"
          />
        </div>
        <div class="step-row">
          <span class="field-label">验证参数</span>
          <span class="wait-label">超时</span>
          <el-input-number
            v-model="getVerify(step).timeout"
            :min="0"
            :step="1000"
            size="small"
            controls-position="right"
            style="width: 130px"
          />
          <span class="wait-label">ms</span>
          <el-checkbox v-model="getVerify(step).must">
            失败即失败(must)
          </el-checkbox>
        </div>
      </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, reactive } from 'vue'
import { ElMessage } from 'element-plus'
import { Top, Bottom, Delete, Plus, Search, ArrowRight } from '@element-plus/icons-vue'
import type { IntentStep } from '@/utils/stepParser'
import { elementLocateApi } from '@/api/elementLocate'
import { usePageUrl } from '@/utils/usePageUrl'

/** 状态验证类型选项（与后端 _build_verify_assert 对齐；CaseStepEditor 同款） */
const VERIFY_TYPE_OPTIONS = [
  { label: '文本可见 (text_visible)', value: 'text_visible' },
  { label: '元素可见 (element_visible)', value: 'element_visible' },
  { label: '文本隐藏 (text_hidden)', value: 'text_hidden' },
  { label: '元素存在 (element_exists)', value: 'element_exists' },
  { label: 'URL 包含 (url_contains)', value: 'url_contains' },
  { label: '弹窗提示 (dialog)', value: 'dialog' },
  { label: '输入值相等 (value)', value: 'value' },
  { label: '元素数量 (count)', value: 'count' },
]

/** 验证是否需要选择器（verify 类型口径） */
const verifyNeedsSelector = (type: string): boolean =>
  ['text_visible', 'element_visible', 'text_hidden', 'element_exists', 'value', 'count'].includes(type || '')

/** 验证是否需要期望文本（verify 类型口径） */
const verifyNeedsText = (type: string): boolean =>
  ['text_visible', 'text_hidden', 'url_contains', 'dialog', 'value', 'count'].includes(type || '')

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

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

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

/** 每行的折叠区展开状态（key 为步骤索引） */
const advancedOpen = reactive<Record<number, boolean>>({})

/** 当前正在获取定位的步骤索引 */
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)
}

// ==================== 状态验证编辑 ====================

/** 读取（必要时初始化）步骤的 verify 对象（v-model 直绑，round-trip 保留） */
const getVerify = (step: IntentStep): Record<string, any> => {
  if (!step.verify || typeof step.verify !== 'object') {
    // 直接赋值以触发响应式更新
    step.verify = { type: '', selector: '', text: '', timeout: 0, must: false }
  }
  return step.verify
}

/** 步骤是否已有有效 verify 类型 */
const hasVerify = (step: IntentStep): boolean => !!step.verify && !!step.verify.type

/** verify 类型中文标签（用于折叠按钮文案） */
const verifyTypeLabel = (step: IntentStep): string => {
  const t = getVerify(step).type
  const found = VERIFY_TYPE_OPTIONS.find((o) => o.value === t)
  return found ? found.label : (t || '未设置')
}

/** 展开/收起折叠区 */
const toggleAdvanced = (index: number) => {
  advancedOpen[index] = !advancedOpen[index]
}

// ==================== 元素定位 ====================

/**
 * 获取定位信息（本地 Playwright 方式）
 *
 * 流程：
 * 1. 调用本地元素定位 API（/api/element/locate）
 * 2. 后端启动 Playwright 访问页面、提取元素、匹配定位器
 * 3. 自动填充定位信息到步骤中
 *
 * 优势：
 * - 本地开发可用，无需容器化部署
 * - 响应更快（无 SSH 开销）
 * - 支持离线使用（不依赖 Claude Code）
 */
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 {
    ElMessage.info('正在启动浏览器访问页面...')

    // 调用本地元素定位 API
    const result = await elementLocateApi.locate({
      step_description: step.description,
      page_url: pageUrl.value,
      expected: step.expected,
      auto_login: true,  // 默认自动登录
    })

    if (result.success && result.candidates.length > 0) {
      // 取置信度最高的候选
      const best = result.candidates[0]

      // 自动填充定位信息
      step.locatorType = best.locator_type
      step.locatorValue = best.locator_value

      ElMessage.success(`定位成功！置信度 ${(best.confidence * 100).toFixed(0)}%`)
      emitChange()
    } else {
      ElMessage.warning(result.message || '未找到匹配元素，请手动输入定位信息')
    }
  } catch (error: any) {
    console.error('元素定位失败:', error)
    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;
    }

    .verify-toggle-row {
      padding-top: 6px;
      margin-top: 4px;

      .toggle-icon {
        transition: transform 0.2s;
        &.expanded {
          transform: rotate(90deg);
        }
      }
    }

    .verify-area {
      padding: 10px 10px 6px 10px;
      margin-top: 4px;
      background: #f0f5ff;
      border: 1px solid #d9e6ff;
      border-radius: 4px;
    }
  }

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