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

    <!-- 选区绘制层（跟随鼠标拖拽） -->
    <div
      class="screenshot-selection-layer"
      @mousedown="onMouseDown"
      @mousemove="onMouseMove"
      @mouseup="onMouseUp"
      @mouseleave="onMouseUp"
    >
      <!-- 暗色遮罩（挖空选区） -->
      <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>

        <!-- 8个拖拽手柄（调整后发送） -->
        <div
          v-if="selectionComplete"
          class="screenshot-handle screenshot-handle--tl"
          @mousedown.stop="onHandleMouseDown($event, 'tl')"
        ></div>
        <div
          v-if="selectionComplete"
          class="screenshot-handle screenshot-handle--tr"
          @mousedown.stop="onHandleMouseDown($event, 'tr')"
        ></div>
        <div
          v-if="selectionComplete"
          class="screenshot-handle screenshot-handle--bl"
          @mousedown.stop="onHandleMouseDown($event, 'bl')"
        ></div>
        <div
          v-if="selectionComplete"
          class="screenshot-handle screenshot-handle--br"
          @mousedown.stop="onHandleMouseDown($event, 'br')"
        ></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">
      按住鼠标拖拽选择截图区域，ESC 取消
    </div>
  </div>
</template>

<script setup lang="ts">
/**
 * ScreenshotEditor 组件
 * 做什么：实现微信/企微风格的区域截图功能
 * 为什么：用户反馈截图不好用，需要对标微信/企微的截图体验
 *
 * 交互流程：
 *   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 { ElMessage } from 'element-plus'

// ========== 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 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

// 调整手柄状态
let resizingHandle = ''                // 正在拖拽的手柄：tl/tr/bl/br
let resizeStartX = 0
let resizeStartY = 0
let resizeStartStartX = 0
let resizeStartStartY = 0
let resizeStartEndX = 0
let resizeStartEndY = 0

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

/** 选区左坐标（取 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 {}  // 未选区时全暗
  }
  // 使用 box-shadow 实现挖空效果（外围暗色，选区明亮）
  const left = selectionLeft.value
  const top = selectionTop.value
  const width = selectionWidth.value
  const height = selectionHeight.value
  return {
    boxShadow: `0 0 0 9999px rgba(0, 0, 0, 0.5)`,
    // 选区位置用透明
    background: selecting.value
      ? 'rgba(0, 0, 0, 0.5)'
      : 'transparent',
    // 用 clip-path 挖空选区
    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()
  })

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

onUnmounted(() => {
  document.removeEventListener('keydown', onKeyDown)
})

// 监听 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 onMouseDown(e: MouseEvent): void {
  // 如果选区已完成，不重新开始（除非点在了选区外）
  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
  }
}

/** 调整手柄：鼠标按下 */
function onHandleMouseDown(e: MouseEvent, handle: string): void {
  resizingHandle = handle
  resizeStartX = e.clientX
  resizeStartY = e.clientY
  resizeStartStartX = startX.value
  resizeStartStartY = startY.value
  resizeStartEndX = endX.value
  resizeStartEndY = endY.value

  document.addEventListener('mousemove', onHandleMouseMove)
  document.addEventListener('mouseup', onHandleMouseUp)
}

/** 调整手柄：鼠标移动 */
function onHandleMouseMove(e: MouseEvent): void {
  const dx = e.clientX - resizeStartX
  const dy = e.clientY - resizeStartY

  switch (resizingHandle) {
    case 'tl':
      startX.value = resizeStartStartX + dx
      startY.value = resizeStartStartY + dy
      break
    case 'tr':
      endX.value = resizeStartEndX + dx
      startY.value = resizeStartStartY + dy
      break
    case 'bl':
      startX.value = resizeStartStartX + dx
      endY.value = resizeStartEndY + dy
      break
    case 'br':
      endX.value = resizeStartEndX + dx
      endY.value = resizeStartEndY + dy
      break
  }
}

/** 调整手柄：鼠标释放 */
function onHandleMouseUp(): void {
  resizingHandle = ''
  document.removeEventListener('mousemove', onHandleMouseMove)
  document.removeEventListener('mouseup', onHandleMouseUp)
}

/** 重置选区 */
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) {
    ElMessage.error('截图数据丢失，请重试')
    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) {
      ElMessage.error('截图裁剪失败（无法创建画布）')
      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) {
      ElMessage.error('截图生成失败（裁剪结果为空），请重试')
      return
    }

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

/** 暴露方法给父组件 */
defineExpose({
  resetSelection,
})
</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;
}

/* 暗色遮罩（未选区时全暗，选区后挖空）
   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-handle {
  position: absolute;
  width: 10px;
  height: 10px;
  background: #1989fa;
  border: 2px solid #fff;
  border-radius: 50%;
  z-index: 30;
}
.screenshot-handle--tl { top: -5px; left: -5px; cursor: nw-resize; }
.screenshot-handle--tr { top: -5px; right: -5px; cursor: ne-resize; }
.screenshot-handle--bl { bottom: -5px; left: -5px; cursor: sw-resize; }
.screenshot-handle--br { bottom: -5px; right: -5px; cursor: se-resize; }

/* 工具栏 */
.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>
