<script setup lang="ts">
import { nextTick, onBeforeUnmount, ref, watch } from 'vue'

const props = defineProps<{
  src: string
}>()

const emit = defineEmits<{
  (e: 'save', dataUrl: string): void
  (e: 'cancel'): void
}>()

const canvasRef = ref<HTMLCanvasElement | null>(null)
const containerRef = ref<HTMLDivElement | null>(null)

const brushColor = ref('#ff0000')
const brushSize = ref(3)
const isEraser = ref(false)
const isDrawing = ref(false)
const canUndo = ref(false)

const colorOptions = [
  '#ff0000',
  '#ff6600',
  '#faad14',
  '#52c41a',
  '#246cff',
  '#722ed1',
  '#000000',
]
const sizeOptions = [2, 3, 5, 8]

let ctx: CanvasRenderingContext2D | null = null
let bgImage: HTMLImageElement | null = null
const history: ImageData[] = []
let lastX = 0
let lastY = 0

function loadImage(src: string) {
  return new Promise<HTMLImageElement>((resolve, reject) => {
    const img = new Image()
    img.crossOrigin = 'anonymous'
    img.addEventListener('load', () => resolve(img))
    img.addEventListener('error', reject)
    img.src = src
  })
}

async function initCanvas() {
  if (!canvasRef.value || !props.src) return
  try {
    bgImage = await loadImage(props.src)
  } catch {
    return
  }
  const canvas = canvasRef.value
  const container = containerRef.value
  if (!canvas || !container) return

  const maxW = container.clientWidth - 2
  const maxH = Math.min(window.innerHeight * 0.6, 600)
  const ratio = Math.min(maxW / bgImage.width, maxH / bgImage.height, 1)
  canvas.width = bgImage.width * ratio
  canvas.height = bgImage.height * ratio

  ctx = canvas.getContext('2d')
  if (!ctx) return
  ctx.drawImage(bgImage, 0, 0, canvas.width, canvas.height)
  pushHistory()
}

function pushHistory() {
  if (!ctx || !canvasRef.value) return
  history.push(
    ctx.getImageData(0, 0, canvasRef.value.width, canvasRef.value.height),
  )
  if (history.length > 30) history.shift()
  canUndo.value = history.length > 1
}

function undo() {
  if (!ctx || !canvasRef.value || history.length <= 1) return
  history.pop()
  const prev = history[history.length - 1]
  if (prev) ctx.putImageData(prev, 0, 0)
  canUndo.value = history.length > 1
}

function resetCanvas() {
  if (!ctx || !canvasRef.value || !bgImage) return
  ctx.clearRect(0, 0, canvasRef.value.width, canvasRef.value.height)
  ctx.drawImage(bgImage, 0, 0, canvasRef.value.width, canvasRef.value.height)
  history.length = 0
  pushHistory()
}

function getPos(e: MouseEvent | TouchEvent) {
  const canvas = canvasRef.value
  if (!canvas) return { x: 0, y: 0 }
  const rect = canvas.getBoundingClientRect()
  const scaleX = canvas.width / rect.width
  const scaleY = canvas.height / rect.height
  if ('touches' in e) {
    const touch = e.touches[0]!
    return {
      x: (touch.clientX - rect.left) * scaleX,
      y: (touch.clientY - rect.top) * scaleY,
    }
  }
  return {
    x: (e.clientX - rect.left) * scaleX,
    y: (e.clientY - rect.top) * scaleY,
  }
}

function startDraw(e: MouseEvent | TouchEvent) {
  if (!ctx) return
  isDrawing.value = true
  const pos = getPos(e)
  lastX = pos.x
  lastY = pos.y
  ctx.beginPath()
  ctx.moveTo(lastX, lastY)
}

function draw(e: MouseEvent | TouchEvent) {
  if (!isDrawing.value || !ctx) return
  e.preventDefault()
  const pos = getPos(e)
  ctx.lineWidth = brushSize.value
  ctx.lineCap = 'round'
  ctx.lineJoin = 'round'
  if (isEraser.value) {
    ctx.globalCompositeOperation = 'destination-out'
    ctx.strokeStyle = 'rgba(0,0,0,1)'
  } else {
    ctx.globalCompositeOperation = 'source-over'
    ctx.strokeStyle = brushColor.value
  }
  ctx.beginPath()
  ctx.moveTo(lastX, lastY)
  ctx.lineTo(pos.x, pos.y)
  ctx.stroke()
  lastX = pos.x
  lastY = pos.y
}

function stopDraw() {
  if (!isDrawing.value) return
  isDrawing.value = false
  if (isEraser.value && bgImage && ctx && canvasRef.value) {
    ctx.globalCompositeOperation = 'destination-over'
    ctx.drawImage(bgImage, 0, 0, canvasRef.value.width, canvasRef.value.height)
    ctx.globalCompositeOperation = 'source-over'
  }
  pushHistory()
}

function selectEraser() {
  isEraser.value = !isEraser.value
}

function selectBrush() {
  isEraser.value = false
}

function selectColor(color: string) {
  brushColor.value = color
  isEraser.value = false
}

function handleSave() {
  if (!canvasRef.value) return
  const dataUrl = canvasRef.value.toDataURL('image/png')
  emit('save', dataUrl)
}

function handleCancel() {
  emit('cancel')
}

watch(
  () => props.src,
  () => {
    nextTick(initCanvas)
  },
  { immediate: true },
)

onBeforeUnmount(() => {
  history.length = 0
  ctx = null
  bgImage = null
})
</script>

<template>
  <div class="image-annotator">
    <div class="annotator-toolbar">
      <div class="tool-group">
        <span class="tool-label">画笔</span>
        <el-button
          :type="!isEraser ? 'primary' : 'default'"
          size="small"
          @click="selectBrush"
        >
          ✏️ 画笔
        </el-button>
        <el-button
          :type="isEraser ? 'primary' : 'default'"
          size="small"
          @click="selectEraser"
        >
          🧹 橡皮
        </el-button>
      </div>
      <div class="tool-group">
        <span class="tool-label">颜色</span>
        <span
          v-for="c in colorOptions"
          :key="c"
          class="color-swatch"
          :class="{ active: brushColor === c && !isEraser }"
          :style="{ backgroundColor: c }"
          @click="selectColor(c)"
        ></span>
      </div>
      <div class="tool-group">
        <span class="tool-label">粗细</span>
        <span
          v-for="s in sizeOptions"
          :key="s"
          class="size-dot"
          :class="{ active: brushSize === s }"
          @click="brushSize = s"
        >
          <span
            class="dot-inner"
            :style="{ width: `${s + 4}px`, height: `${s + 4}px` }"
          ></span>
        </span>
      </div>
      <div class="tool-group tool-actions">
        <el-button size="small" :disabled="!canUndo" @click="undo">
          ↩ 撤销
        </el-button>
        <el-button size="small" @click="resetCanvas">🔄 重置</el-button>
      </div>
    </div>
    <div ref="containerRef" class="annotator-canvas-wrapper">
      <canvas
        ref="canvasRef"
        class="annotator-canvas"
        @mousedown="startDraw"
        @mousemove="draw"
        @mouseup="stopDraw"
        @mouseleave="stopDraw"
        @touchstart.prevent="startDraw"
        @touchmove.prevent="draw"
        @touchend="stopDraw"
      ></canvas>
    </div>
    <div class="annotator-footer">
      <el-button @click="handleCancel">取消</el-button>
      <el-button type="primary" @click="handleSave">
        ✅ 确认保存涂鸦
      </el-button>
    </div>
  </div>
</template>

<style scoped>
.image-annotator {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.annotator-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  align-items: center;
  padding: 10px 14px;
  background-color: #f6f8fa;
  border: 1px solid #eef0f2;
  border-radius: 8px;
}

.tool-group {
  display: flex;
  gap: 6px;
  align-items: center;
}

.tool-label {
  margin-right: 4px;
  font-size: 13px;
  font-weight: 600;
  color: #667085;
  white-space: nowrap;
}

.color-swatch {
  display: inline-block;
  width: 22px;
  height: 22px;
  cursor: pointer;
  border: 2px solid transparent;
  border-radius: 50%;
  transition: all 0.15s;
}

.color-swatch:hover {
  transform: scale(1.2);
}

.color-swatch.active {
  border-color: #246cff;
  box-shadow: 0 0 0 2px rgb(36 108 255 / 30%);
  transform: scale(1.2);
}

.size-dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  cursor: pointer;
  border: 2px solid transparent;
  border-radius: 50%;
  transition: all 0.15s;
}

.size-dot.active {
  background-color: #e8f0fe;
  border-color: #246cff;
}

.dot-inner {
  display: block;
  background-color: #1f2328;
  border-radius: 50%;
}

.tool-actions {
  margin-left: auto;
}

.annotator-canvas-wrapper {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 4px;
  overflow: auto;
  background-color: #f0f0f0;
  border: 1px solid #d9d9d9;
  border-radius: 8px;
}

.annotator-canvas {
  display: block;
  max-width: 100%;
  cursor: crosshair;
  border-radius: 4px;
  box-shadow: 0 2px 8px rgb(0 0 0 / 12%);
}

.annotator-footer {
  display: flex;
  gap: 10px;
  justify-content: flex-end;
}
</style>
