<template>
  <div class="slider-captcha">
    <!-- 触发按钮（未验证时显示） -->
    <el-button
      v-if="!verified"
      type="warning"
      size="large"
      plain
      style="width:100%; margin-bottom:12px"
      :loading="loading"
      @click="openDialog"
    >
      ⚠ 请完成安全验证
    </el-button>

    <!-- 已验证 -->
    <div v-else class="verified-badge">
      ✅ 安全验证通过
    </div>

    <!-- 验证对话框 -->
    <el-dialog
      v-model="dialogVisible"
      title="安全验证"
      width="380px"
      :close-on-click-modal="false"
      append-to-body
      top="20vh"
    >
      <div class="captcha-body">
        <!-- 图形验证码模式 -->
        <template v-if="captchaType === 'image'">
          <div class="image-captcha-wrapper">
            <img :src="captchaImage" class="captcha-img" alt="验证码" />
            <el-input
              v-model="imageAnswer"
              placeholder="输入验证码"
              size="large"
              maxlength="4"
              style="width:120px"
            />
          </div>
          <div class="captcha-actions">
            <el-button size="small" @click="fetchCaptcha">换一张</el-button>
            <el-button type="primary" size="small" @click="verifyImageCaptcha">确认</el-button>
          </div>
        </template>

        <!-- 滑块拼图模式 -->
        <template v-else>
          <div class="slider-wrapper">
            <div class="slider-bg">
              <img :src="sliderBg" alt="滑块背景" />
              <div
                class="slider-gap-marker"
                :style="{ left: sliderGapX + 'px' }"
              ></div>
            </div>
            <div class="slider-piece-track">
              <div
                class="slider-piece"
                :style="{ left: sliderPos + 'px', background: dragging ? '#409eff' : '#67c23a' }"
              ></div>
              <div
                class="slider-rail"
                ref="railRef"
                @mousedown.prevent="startDrag"
                @touchstart.prevent="startDrag"
              >
                <div class="slider-thumb" :style="{ left: sliderPos + 'px' }">
                  {{ dragging ? '→' : '→' }}
                </div>
              </div>
            </div>
            <p class="slider-hint">{{ dragging ? '松开完成验证' : '拖动滑块完成拼图' }}</p>
          </div>
          <div class="captcha-actions">
            <el-button size="small" @click="fetchCaptcha">换一张</el-button>
          </div>
        </template>
      </div>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { securityApi } from '../api/index'

const emit = defineEmits<{
  (e: 'verified', token: string): void
}>()

const loading = ref(false)
const dialogVisible = ref(false)
const verified = ref(false)
const captchaToken = ref('')
const captchaType = ref<'image' | 'slider'>('slider')

// 图形验证码
const captchaImage = ref('')
const imageAnswer = ref('')
const captchaId = ref('')

// 滑块拼图
const sliderBg = ref('')
const sliderGapX = ref(0)
const sliderPos = ref(0)
const dragging = ref(false)
const railRef = ref<HTMLElement | null>(null)
const dragStartX = ref(0)
const dragStartPos = ref(0)

// 容差像素
const TOLERANCE = 6

async function fetchCaptcha() {
  loading.value = true
  try {
    const res: any = await securityApi.requestCaptcha(captchaType.value)
    captchaId.value = res.captcha_id
    if (captchaType.value === 'image') {
      captchaImage.value = res.image
      imageAnswer.value = ''
    } else {
      sliderBg.value = res.bg
      sliderGapX.value = res.gap_x
      sliderPos.value = 0
    }
  } catch {
    ElMessage.error('获取验证码失败')
  }
  loading.value = false
}

async function openDialog() {
  captchaType.value = Math.random() > 0.3 ? 'slider' : 'image'
  dialogVisible.value = true
  await fetchCaptcha()
}

async function verifyImageCaptcha() {
  if (!imageAnswer.value || imageAnswer.value.length !== 4) {
    ElMessage.warning('请输入4位验证码')
    return
  }
  loading.value = true
  try {
    const res: any = await securityApi.verifyCaptcha({
      captcha_id: captchaId.value,
      answer: imageAnswer.value.toUpperCase(),
      captcha_type: 'image',
    })
    captchaToken.value = res.token
    verified.value = true
    dialogVisible.value = false
    emit('verified', res.token)
    ElMessage.success('验证通过')
  } catch (e: any) {
    ElMessage.error(e?.response?.data?.detail || '验证失败')
    await fetchCaptcha()
  }
  loading.value = false
}

function startDrag(e: MouseEvent | TouchEvent) {
  dragging.value = true
  const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX
  dragStartX.value = clientX
  dragStartPos.value = sliderPos.value

  document.addEventListener('mousemove', onDrag)
  document.addEventListener('mouseup', endDrag)
  document.addEventListener('touchmove', onDrag, { passive: false })
  document.addEventListener('touchend', endDrag)
}

function onDrag(e: MouseEvent | TouchEvent) {
  if (!dragging.value) return
  const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX
  const rail = railRef.value
  if (!rail) return
  const railRect = rail.getBoundingClientRect()
  const max = railRect.width - 40
  let newPos = dragStartPos.value + (clientX - dragStartX.value)
  newPos = Math.max(0, Math.min(newPos, max))
  sliderPos.value = newPos
}

function endDrag() {
  if (!dragging.value) return
  dragging.value = false
  document.removeEventListener('mousemove', onDrag)
  document.removeEventListener('mouseup', endDrag)
  document.removeEventListener('touchmove', onDrag)
  document.removeEventListener('touchend', endDrag)

  // 验证滑块位置是否匹配缺口位置
  const rail = railRef.value
  if (!rail) return
  const railWidth = rail.getBoundingClientRect().width - 40
  const piecePos = (sliderPos.value / railWidth) * 280 // 映射到280px背景图宽度
  const gapX = sliderGapX.value
  if (Math.abs(piecePos - gapX) <= TOLERANCE) {
    // 验证通过 → 调用后端确认
    confirmSlider()
  } else {
    ElMessage.warning('位置不匹配，请再试一次')
    sliderPos.value = 0
  }
}

async function confirmSlider() {
  loading.value = true
  try {
    const res: any = await securityApi.verifyCaptcha({
      captcha_id: captchaId.value,
      answer: 'verified',
      captcha_type: 'slider',
    })
    captchaToken.value = res.token
    verified.value = true
    dialogVisible.value = false
    emit('verified', res.token)
    ElMessage.success('验证通过')
  } catch (e: any) {
    ElMessage.error(e?.response?.data?.detail || '验证失败')
    sliderPos.value = 0
    await fetchCaptcha()
  }
  loading.value = false
}

// 对外暴露重置方法
function reset() {
  verified.value = false
  captchaToken.value = ''
}

defineExpose({ reset })
</script>

<style scoped>
.captcha-body {
  text-align: center;
}
.image-captcha-wrapper {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin-bottom: 12px;
}
.captcha-img {
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  height: 50px;
}
.captcha-actions {
  display: flex;
  justify-content: center;
  gap: 8px;
  margin-top: 8px;
}
.slider-wrapper {
  margin-bottom: 8px;
}
.slider-bg {
  position: relative;
  margin: 0 auto 12px;
  width: 280px;
  height: 160px;
  overflow: hidden;
  border-radius: 4px;
}
.slider-bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.slider-gap-marker {
  position: absolute;
  top: 0;
  width: 40px;
  height: 40px;
  border: 2px solid rgba(255, 0, 0, 0.5);
  background: rgba(255, 0, 0, 0.1);
  pointer-events: none;
}
.slider-piece-track {
  position: relative;
  margin: 0 auto 8px;
  width: 280px;
}
.slider-piece {
  position: absolute;
  top: -40px;
  width: 40px;
  height: 40px;
  border-radius: 4px;
  z-index: 2;
  transition: none;
}
.slider-rail {
  position: relative;
  width: 100%;
  height: 36px;
  background: #ebeef5;
  border-radius: 18px;
  cursor: pointer;
  user-select: none;
}
.slider-thumb {
  position: absolute;
  top: -2px;
  width: 40px;
  height: 40px;
  line-height: 40px;
  text-align: center;
  background: #409eff;
  color: #fff;
  border-radius: 50%;
  cursor: grab;
  font-size: 16px;
  box-shadow: 0 2px 6px rgba(0,0,0,0.15);
}
.slider-thumb:active { cursor: grabbing; }
.slider-hint { color: #909399; font-size: 12px; margin: 0; }
.verified-badge {
  color: #67c23a;
  font-size: 14px;
  margin-bottom: 12px;
}
</style>
