<template>
  <!--
    ScreenshotEditor H5 版 - 区域选择截图组件
    对标微信/企微截图体验，适配触摸操作
    1. 全屏遮罩，背景显示页面截图（变暗）
    2. 触摸拖拽框选区域
    3. 释放后显示选区和工具栏（确认/取消/重新选择）
    4. 确认后裁剪选中区域并 emit 回去
  -->
  <div class="screenshot-editor-overlay">
    <!-- 背景：页面截图（变暗） -->
    <canvas
      ref="canvasBgRef"
      class="screenshot-editor-bg"
    ></canvas>

    <!-- 选区绘制层（跟随触摸拖拽） -->
    <!--
      注意：触摸事件不使用 Vue 的 @touchstart/@touchmove 绑定，
      因为 Vue 在移动端默认用 passive 模式绑定，无法调用 preventDefault()。
      改为在 onMounted 中用 addEventListener 手动绑定非 passive 监听器。
    -->
    <div
      ref="selectionLayerRef"
      class="screenshot-selection-layer"
    >
      <!-- 暗色遮罩 -->
      <div class="screenshot-dark-overlay" :style="darkOverlayStyle"></div>

      <!-- 选区边框 -->
      <div
        v-if="selecting || selectionComplete"
        class="screenshot-selection-box"
        :style="selectionBoxStyle"
      >
        <!-- 选区尺寸提示 -->
        <div class="screenshot-size-tip" v-if="selectionComplete">
          {{ selectionWidth }} × {{ selectionHeight }}
        </div>
      </div>
    </div>

    <!-- 工具栏（选区完成后显示） -->
    <div v-if="selectionComplete" class="screenshot-toolbar">
      <button class="screenshot-toolbar-btn" @click="handleCancel" title="取消截图">
        取消
      </button>
      <button class="screenshot-toolbar-btn" @click="handleReselect" title="重新选择">
        重选
      </button>
      <button class="screenshot-toolbar-btn screenshot-toolbar-btn--primary" @click="handleConfirm" title="确认截图">
        确认
      </button>
    </div>

    <!-- 提示文字（未选区时显示） -->
    <div v-if="!selecting && !selectionComplete" class="screenshot-tip">
      按住屏幕拖拽选择截图区域，返回键 取消
    </div>
  </div>
</template>

<script setup lang="ts">
/**
 * ScreenshotEditor H5 版
 * 做什么：实现触摸友好的区域截图功能
 * 为什么：对标微信/企微截图体验，适配移动端
 *
 * 交互流程：
 *   1. 传入页面截图的 canvas/image
 *   2. 用户触摸拖拽选择区域
 *   3. 确认后裁剪选中区域，通过 emit 返回 Blob
 *   4. 取消则关闭编辑器
 *
 * Props:
 *   - visible: 是否显示编辑器
 *   - screenshotCanvas: html2canvas 生成的完整页面截图（Canvas）
 *
 * Emits:
 *   - confirm: 确认截图，参数是裁剪后图片的 Blob
 *   - cancel: 取消截图
 */
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
import { showToast } from 'vant'

// ========== Props & Emits ==========
interface Props {
  /** html2canvas 生成的完整页面截图 Canvas 对象 */
  screenshotCanvas: HTMLCanvasElement | null
}

const props = withDefaults(defineProps<Props>(), {
  screenshotCanvas: null,
})

const emit = defineEmits<{
  (e: 'confirm', blob: Blob): void
  (e: 'cancel'): void
}>()

// ========== Refs ==========
const canvasBgRef = ref<HTMLCanvasElement | null>(null)
const selectionLayerRef = ref<HTMLDivElement | null>(null)  // 选区绘制层 DOM 引用

// 选区状态
const selecting = ref(false)           // 是否正在拖拽选区
const selectionComplete = ref(false)    // 选区是否完成
const startX = ref(0)                 // 拖拽起点 X
const startY = ref(0)                 // 拖拽起点 Y
const endX = ref(0)                   // 拖拽终点 X
const endY = ref(0)                   // 拖拽终点 Y

// ========== 计算属性 ==========

/** 选区左坐标（取 start/end 最小值） */
const selectionLeft = computed(() => Math.min(startX.value, endX.value))
/** 选区上坐标 */
const selectionTop = computed(() => Math.min(startY.value, endY.value))
/** 选区宽度 */
const selectionWidth = computed(() => Math.abs(endX.value - startX.value))
/** 选区高度 */
const selectionHeight = computed(() => Math.abs(endY.value - startY.value))

/** 选区盒模型样式 */
const selectionBoxStyle = computed(() => ({
  left: `${selectionLeft.value}px`,
  top: `${selectionTop.value}px`,
  width: `${selectionWidth.value}px`,
  height: `${selectionHeight.value}px`,
}))

/** 暗色遮罩样式 */
const darkOverlayStyle = computed(() => {
  if (!selectionComplete.value && !selecting.value) {
    return {}  // 未选区时全暗
  }
  // 使用 clip-path 挖空选区
  const left = selectionLeft.value
  const top = selectionTop.value
  const width = selectionWidth.value
  const height = selectionHeight.value
  return {
    clipPath: selecting.value
      ? 'none'
      : `polygon(0% 0%, 0% 100%, ${left}px ${top}px, ${left}px ${top + height}px, ${left + width}px ${top + height}px, ${left + width}px ${top}px, 0% 100%, 100% 100%, 100% 0%)`,
  }
})

// ========== 生命周期 ==========
onMounted(() => {
  // 将截图绘制到背景 canvas
  nextTick(() => {
    drawBackground()
  })

  const layer = selectionLayerRef.value
  if (!layer) {
    console.error('[ScreenshotEditor] selectionLayerRef 为 null，无法绑定事件')
    return
  }

  // ========= 触摸事件（移动端）=========
  // 手动绑定（非 passive 模式，允许 preventDefault 阻止页面滚动）
  // Vue 的 @touchstart/@touchmove 在移动端默认 passive，无法调用 preventDefault()
  layer.addEventListener('touchstart', onTouchStart, { passive: false })
  layer.addEventListener('touchmove', onTouchMove, { passive: false })
  layer.addEventListener('touchend', onTouchEnd)

  // ========= 鼠标事件（桌面端 H5，企微桌面端占 70%）=========
  // 鼠标拖拽拖选区域（对标微信/企微桌面端截图体验）
  layer.addEventListener('mousedown', onMouseDown)
  // mousemove/mouseup 绑定在 document 上，防止拖拽出选区层后丢失事件
  document.addEventListener('mousemove', onMouseMove)
  document.addEventListener('mouseup', onMouseUp)

  console.log('[ScreenshotEditor] 事件绑定完成（触摸 + 鼠标）')

  // 监听 ESC 键取消
  document.addEventListener('keydown', onKeyDown)
})

onUnmounted(() => {
  const layer = selectionLayerRef.value

  // 清除触摸事件
  if (layer) {
    layer.removeEventListener('touchstart', onTouchStart)
    layer.removeEventListener('touchmove', onTouchMove)
    layer.removeEventListener('touchend', onTouchEnd)
  }

  // 清除鼠标事件（绑定在 document 上的也要清）
  document.removeEventListener('mousemove', onMouseMove)
  document.removeEventListener('mouseup', onMouseUp)

  document.removeEventListener('keydown', onKeyDown)
  console.log('[ScreenshotEditor] 事件已清除')
})

// 监听 screenshotCanvas 变化，重新绘制背景
watch(
  () => props.screenshotCanvas,
  () => {
    nextTick(() => drawBackground())
  }
)

// ========== 方法 ==========

/** 将截图绘制到背景 canvas */
function drawBackground(): void {
  const canvas = canvasBgRef.value
  if (!canvas || !props.screenshotCanvas) return

  // 设置 canvas 尺寸为窗口大小
  canvas.width = window.innerWidth
  canvas.height = window.innerHeight

  const ctx = canvas.getContext('2d')
  if (!ctx) return

  // 将截图绘制到 canvas（覆盖整个窗口）
  ctx.drawImage(props.screenshotCanvas, 0, 0, canvas.width, canvas.height)
}

/** 键盘事件：ESC 取消 */
function onKeyDown(e: KeyboardEvent): void {
  if (e.key === 'Escape') {
    handleCancel()
  }
}

/** 触摸开始：开始选区 */
function onTouchStart(e: TouchEvent): void {
  // 阻止浏览器默认行为（防止页面滚动/缩放干扰拖拽选区）
  e.preventDefault()

  // 如果选区已完成，判断是否点击在选区外
  if (selectionComplete.value) {
    const touch = e.touches[0]
    const rect = getSelectionRect()
    if (
      touch.clientX < rect.left ||
      touch.clientX > rect.right ||
      touch.clientY < rect.top ||
      touch.clientY > rect.bottom
    ) {
      // 点击在选区外，重新开始选区
      resetSelection()
    } else {
      // 点击在选区内，不处理
      return
    }
  }

  selecting.value = true
  const touch = e.touches[0]
  startX.value = touch.clientX
  startY.value = touch.clientY
  endX.value = touch.clientX
  endY.value = touch.clientY
}

/** 触摸移动：更新选区 */
function onTouchMove(e: TouchEvent): void {
  if (selecting.value) {
    // 阻止默认行为（防止页面滚动干扰拖拽）
    e.preventDefault()
    const touch = e.touches[0]
    endX.value = touch.clientX
    endY.value = touch.clientY
  }
}

/** 触摸结束：完成选区 */
function onTouchEnd(): void {
  if (selecting.value) {
    selecting.value = false

    // 判断选区是否有效（最小 10x10）
    if (selectionWidth.value < 10 || selectionHeight.value < 10) {
      // 选区太小，忽略
      resetSelection()
      return
    }

    selectionComplete.value = true
  }
}

// ========== 鼠标事件处理（桌面端 H5 用）=========

/** 鼠标按下：开始选区 */
function onMouseDown(e: MouseEvent): void {
  console.log('[ScreenshotEditor] mousedown', e.clientX, e.clientY)

  // 如果选区已完成，判断是否点击在选区外
  if (selectionComplete.value) {
    const rect = getSelectionRect()
    if (
      e.clientX < rect.left ||
      e.clientX > rect.right ||
      e.clientY < rect.top ||
      e.clientY > rect.bottom
    ) {
      // 点击在选区外，重新开始选区
      resetSelection()
    } else {
      // 点击在选区内，不处理（允许拖拽移动选区，后续可扩展）
      return
    }
  }

  selecting.value = true
  startX.value = e.clientX
  startY.value = e.clientY
  endX.value = e.clientX
  endY.value = e.clientY
}

/** 鼠标移动：更新选区 */
function onMouseMove(e: MouseEvent): void {
  if (selecting.value) {
    endX.value = e.clientX
    endY.value = e.clientY
  }
}

/** 鼠标抬起：完成选区 */
function onMouseUp(): void {
  if (selecting.value) {
    selecting.value = false

    // 判断选区是否有效（最小 10x10）
    if (selectionWidth.value < 10 || selectionHeight.value < 10) {
      resetSelection()
      return
    }

    selectionComplete.value = true
    console.log('[ScreenshotEditor] 选区完成', getSelectionRect())
  }
}

/** 重置选区 */
function resetSelection(): void {
  selecting.value = false
  selectionComplete.value = false
  startX.value = 0
  startY.value = 0
  endX.value = 0
  endY.value = 0
}

/** 获取选区矩形（绝对坐标） */
function getSelectionRect() {
  return {
    left: selectionLeft.value,
    top: selectionTop.value,
    right: selectionLeft.value + selectionWidth.value,
    bottom: selectionTop.value + selectionHeight.value,
    width: selectionWidth.value,
    height: selectionHeight.value,
  }
}

/** 取消截图 */
function handleCancel(): void {
  resetSelection()
  emit('cancel')
}

/** 重新选择 */
function handleReselect(): void {
  resetSelection()
}

/** 确认截图：裁剪选中区域并 emit Blob */
async function handleConfirm(): Promise<void> {
  if (!props.screenshotCanvas) {
    showToast('截图数据丢失，请重试')
    return
  }

  try {
    // 计算缩放因子（screenshotCanvas 可能包含 devicePixelRatio）
    const scaleX = props.screenshotCanvas.width / window.innerWidth
    const scaleY = props.screenshotCanvas.height / window.innerHeight

    // 用 canvas 裁剪选中区域
    const cropCanvas = document.createElement('canvas')
    cropCanvas.width = selectionWidth.value
    cropCanvas.height = selectionHeight.value

    const ctx = cropCanvas.getContext('2d')
    if (!ctx) {
      showToast('截图裁剪失败（无法创建画布）')
      return
    }

    // 从完整截图中裁剪选中区域（使用缩放后的坐标）
    ctx.drawImage(
      props.screenshotCanvas,
      selectionLeft.value * scaleX,
      selectionTop.value * scaleY,
      selectionWidth.value * scaleX,
      selectionHeight.value * scaleY,
      0,
      0,
      selectionWidth.value,
      selectionHeight.value
    )

    // 转为 Blob
    const blob = await new Promise<Blob | null>((resolve) => {
      cropCanvas.toBlob((b) => resolve(b), 'image/png')
    })

    if (!blob || blob.size === 0) {
      showToast('截图生成失败（裁剪结果为空），请重试')
      return
    }

    emit('confirm', blob)
    resetSelection()
  } catch (err) {
    console.error('[ScreenshotEditor] 确认截图失败:', err)
    showToast('截图确认失败，请重试')
  }
}
</script>

<style scoped>
/* 全屏遮罩 */
.screenshot-editor-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  z-index: 9999;
  cursor: crosshair;
  user-select: none;
}

/* 背景 canvas（显示页面截图） */
.screenshot-editor-bg {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* 选区绘制层 */
.screenshot-selection-layer {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 10;
  touch-action: none;          /* 阻止浏览器默认触摸手势（滚动/缩放），确保触摸事件到达 js */
  -webkit-touch-callout: none;  /* 禁止长按弹出菜单 */
}

/* 暗色遮罩（未选区时全暗，选区后挖空）
   pointer-events: none — 让触摸事件穿透遮罩，到达选区层 */
.screenshot-dark-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.5);
  z-index: 1;
  pointer-events: none;
}

/* 选区边框 */
.screenshot-selection-box {
  position: absolute;
  border: 2px solid #1989fa;
  background: rgba(25, 137, 250, 0.05);
  z-index: 20;
  box-sizing: border-box;
}

/* 选区尺寸提示 */
.screenshot-size-tip {
  position: absolute;
  bottom: -24px;
  left: 50%;
  transform: translateX(-50%);
  background: rgba(0, 0, 0, 0.7);
  color: #fff;
  font-size: 12px;
  padding: 2px 8px;
  border-radius: 4px;
  white-space: nowrap;
  pointer-events: none;
}

/* 工具栏 */
.screenshot-toolbar {
  position: fixed;
  bottom: 40px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 12px;
  padding: 10px 20px;
  background: rgba(0, 0, 0, 0.8);
  border-radius: 8px;
  z-index: 100;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
}

.screenshot-toolbar-btn {
  padding: 6px 16px;
  border: 1px solid rgba(255, 255, 255, 0.3);
  background: transparent;
  color: #fff;
  font-size: 13px;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.2s;
}
.screenshot-toolbar-btn:hover {
  background: rgba(255, 255, 255, 0.1);
}
.screenshot-toolbar-btn--primary {
  background: #1989fa;
  border-color: #1989fa;
}
.screenshot-toolbar-btn--primary:hover {
  background: #0570db;
}

/* 提示文字 */
.screenshot-tip {
  position: fixed;
  top: 20px;
  left: 50%;
  transform: translateX(-50%);
  background: rgba(0, 0, 0, 0.7);
  color: #fff;
  font-size: 13px;
  padding: 8px 16px;
  border-radius: 6px;
  pointer-events: none;
  z-index: 100;
}
</style>
