<template>
  <div class="slide-canvas-wrapper" :style="wrapperStyle">
    <canvas
      ref="canvasRef"
      :width="canvasWidth"
      :height="canvasHeight"
      class="slide-canvas"
      role="img"
      :aria-label="`幻灯片 ${(slide?.index ?? 0) + 1}`"
      @click="handleClick"
      @dblclick="handleDoubleClick"
      @mousemove="handleMouseMove"
    />
    <!-- 行内文字编辑器 -->
    <div
      v-if="editingShape || editingCell"
      class="text-editor-overlay"
      :style="editorStyle"
    >
      <textarea
        ref="editorRef"
        v-model="editingText"
        class="text-editor"
        :style="editorTextStyle"
        @blur="commitEdit"
        @keydown.escape="cancelEdit"
        @keydown.ctrl.enter="commitEdit"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, watch, onMounted, nextTick } from 'vue'
import type { SlideData, SlideShape, SlideParagraph } from '@/utils/ppt-api'

const props = defineProps<{
  slide: SlideData | null
  slideWidth?: number
  slideHeight?: number
  scale?: number
  editable?: boolean
  selectedShapeId?: string | null
}>()

const emit = defineEmits<{
  'update:shapes': [shapes: SlideShape[]]
  'shape-select': [shape: SlideShape | null, index: number]
}>()

const canvasRef = ref<HTMLCanvasElement | null>(null)
const editorRef = ref<HTMLTextAreaElement | null>(null)

// 编辑状态
const editingShape = ref<SlideShape | null>(null)
const editingText = ref('')
const editingShapeIndex = ref(-1)
const editingCell = ref<{ shape: SlideShape; shapeIndex: number; row: number; col: number } | null>(null)

// 画布尺寸
const baseWidth = computed(() => props.slideWidth || 960)
const baseHeight = computed(() => props.slideHeight || 540)
const scale = computed(() => props.scale ?? 1)
const canvasWidth = computed(() => Math.round(baseWidth.value * scale.value))
const canvasHeight = computed(() => Math.round(baseHeight.value * scale.value))

const wrapperStyle = computed(() => ({
  width: `${canvasWidth.value}px`,
  height: `${canvasHeight.value}px`,
  position: 'relative' as const,
}))

// 编辑器定位
const editorStyle = computed(() => {
  if (editingCell.value) {
    const { shape, row, col } = editingCell.value
    const rows = shape.rows || []
    const cellH = Math.max(20, shape.h / Math.max(rows.length, 1))
    const cellW = shape.w / Math.max(rows[0]?.length || 1, 1)
    return {
      left: `${(shape.x + col * cellW) * scale.value}px`,
      top: `${(shape.y + row * cellH) * scale.value}px`,
      width: `${cellW * scale.value}px`,
      minHeight: `${cellH * scale.value}px`,
    }
  }
  if (!editingShape.value) return {}
  const s = editingShape.value
  return {
    left: `${s.x * scale.value}px`,
    top: `${s.y * scale.value}px`,
    width: `${s.w * scale.value}px`,
    minHeight: `${s.h * scale.value}px`,
  }
})

const editorTextStyle = computed(() => {
  if (editingCell.value) {
    const cell = editingCell.value.shape.rows?.[editingCell.value.row]?.[editingCell.value.col]
    return {
      fontSize: `${(cell?.fontSize || 12) * scale.value}px`,
      fontWeight: cell?.bold ? 'bold' : 'normal',
    }
  }
  if (!editingShape.value?.paragraphs?.length) return {}
  const p = editingShape.value.paragraphs[0]
  return {
    fontSize: `${(p.fontSize || 14) * scale.value}px`,
    fontWeight: p.bold ? 'bold' : 'normal',
    color: p.color || '#333',
    textAlign: p.align || 'left',
  }
})

// 渲染
function render() {
  const canvas = canvasRef.value
  if (!canvas || !props.slide) return
  const ctx = canvas.getContext('2d')
  if (!ctx) return

  const s = scale.value
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  ctx.save()
  ctx.scale(s, s)

  // 背景
  const bg = props.slide.background || '#ffffff'
  ctx.fillStyle = bg
  ctx.fillRect(0, 0, baseWidth.value, baseHeight.value)

  // 形状
  for (const shape of props.slide.shapes) {
    renderShape(ctx, shape)
  }
  const selected = props.slide.shapes.find(shape => shape.id === props.selectedShapeId)
  if (selected) {
    ctx.strokeStyle = '#3B7A57'
    ctx.lineWidth = 2 / s
    ctx.setLineDash([6 / s, 4 / s])
    ctx.strokeRect(selected.x, selected.y, selected.w, selected.h)
    ctx.setLineDash([])
  }

  ctx.restore()
}

function renderShape(ctx: CanvasRenderingContext2D, shape: SlideShape) {
  if (shape.type === 'group' && shape.children) {
    for (const child of shape.children) {
      renderShape(ctx, child)
    }
    return
  }

  // 填充背景
  if (shape.fill) {
    ctx.fillStyle = shape.fill
    ctx.fillRect(shape.x, shape.y, shape.w, shape.h)
  }

  // 边框
  if (shape.lineColor) {
    ctx.strokeStyle = shape.lineColor
    ctx.lineWidth = shape.lineWidth || 1
    ctx.strokeRect(shape.x, shape.y, shape.w, shape.h)
  }

  // 文本
  if (shape.type === 'textbox' && shape.paragraphs) {
    renderText(ctx, shape)
  }

  // 表格
  if (shape.type === 'table' && shape.rows) {
    renderTable(ctx, shape)
  }

  // 图片占位
  if (shape.type === 'image') {
    renderImagePlaceholder(ctx, shape)
  }
}

function renderText(ctx: CanvasRenderingContext2D, shape: SlideShape) {
  const paragraphs = shape.paragraphs || []
  let y = shape.y + 4 // 顶部内边距
  const maxWidth = shape.w - 8

  for (const para of paragraphs) {
    if (!para.text) {
      y += (para.fontSize || 14) * 1.4
      continue
    }

    const fontSize = para.fontSize || 14
    const fontWeight = para.bold ? 'bold' : 'normal'
    const color = para.color || '#333333'
    const align = para.align || 'left'

    ctx.font = `${fontWeight} ${fontSize}px -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif`
    ctx.fillStyle = color
    ctx.textBaseline = 'top'

    // 自动换行
    const lineHeight = fontSize * 1.4
    const words = para.text.split('')
    let line = ''
    let lineX = shape.x + 4

    for (const char of words) {
      const testLine = line + char
      const metrics = ctx.measureText(testLine)
      if (metrics.width > maxWidth && line) {
        // 绘制当前行
        let drawX = lineX
        if (align === 'center') {
          drawX = shape.x + (shape.w - ctx.measureText(line).width) / 2
        } else if (align === 'right') {
          drawX = shape.x + shape.w - 4 - ctx.measureText(line).width
        }
        ctx.fillText(line, drawX, y)
        line = char
        y += lineHeight
      } else {
        line = testLine
      }
    }
    if (line) {
      let drawX = lineX
      if (align === 'center') {
        drawX = shape.x + (shape.w - ctx.measureText(line).width) / 2
      } else if (align === 'right') {
        drawX = shape.x + shape.w - 4 - ctx.measureText(line).width
      }
      ctx.fillText(line, drawX, y)
      y += lineHeight
    }
  }
}

function renderTable(ctx: CanvasRenderingContext2D, shape: SlideShape) {
  const rows = shape.rows || []
  if (rows.length === 0) return

  const cellH = Math.max(20, shape.h / rows.length)
  const cellW = shape.w / (rows[0]?.length || 1)

  for (let r = 0; r < rows.length; r++) {
    for (let c = 0; c < rows[r].length; c++) {
      const cell = rows[r][c]
      const cx = shape.x + c * cellW
      const cy = shape.y + r * cellH

      // 单元格背景（表头行深色）
      ctx.fillStyle = r === 0 ? '#f0f4f8' : '#ffffff'
      ctx.fillRect(cx, cy, cellW, cellH)
      ctx.strokeStyle = '#e0e0e0'
      ctx.lineWidth = 0.5
      ctx.strokeRect(cx, cy, cellW, cellH)

      // 文本
      const fontSize = cell.fontSize || 12
      ctx.font = `${cell.bold ? 'bold' : 'normal'} ${fontSize}px -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif`
      ctx.fillStyle = '#333'
      ctx.textBaseline = 'middle'
      ctx.fillText(cell.text || '', cx + 4, cy + cellH / 2)
    }
  }
}

function renderImagePlaceholder(ctx: CanvasRenderingContext2D, shape: SlideShape) {
  ctx.fillStyle = '#e8edf2'
  ctx.fillRect(shape.x, shape.y, shape.w, shape.h)
  ctx.strokeStyle = '#c0c8d4'
  ctx.lineWidth = 1
  ctx.strokeRect(shape.x, shape.y, shape.w, shape.h)

  // 图片图标
  const cx = shape.x + shape.w / 2
  const cy = shape.y + shape.h / 2
  const iconSize = Math.min(shape.w, shape.h) * 0.3
  ctx.fillStyle = '#a0aec0'
  ctx.beginPath()
  ctx.moveTo(cx - iconSize * 0.5, cy + iconSize * 0.3)
  ctx.lineTo(cx - iconSize * 0.2, cy - iconSize * 0.1)
  ctx.lineTo(cx + iconSize * 0.1, cy + iconSize * 0.2)
  ctx.lineTo(cx + iconSize * 0.3, cy - iconSize * 0.2)
  ctx.lineTo(cx + iconSize * 0.5, cy + iconSize * 0.3)
  ctx.closePath()
  ctx.fill()

  ctx.font = '11px sans-serif'
  ctx.fillStyle = '#8899aa'
  ctx.textAlign = 'center'
  ctx.textBaseline = 'top'
  ctx.fillText('图片', cx, cy + iconSize * 0.5)
  ctx.textAlign = 'left'
}

// 点击处理
function handleClick(e: MouseEvent) {
  if (!props.editable || !props.slide) return

  const rect = canvasRef.value!.getBoundingClientRect()
  const x = (e.clientX - rect.left) / scale.value
  const y = (e.clientY - rect.top) / scale.value

  const hit = findShapeAt(x, y)
  emit('shape-select', hit?.shape || null, hit?.index ?? -1)
}

function handleDoubleClick(e: MouseEvent) {
  if (!props.editable || !props.slide) return
  const rect = canvasRef.value!.getBoundingClientRect()
  const x = (e.clientX - rect.left) / scale.value
  const y = (e.clientY - rect.top) / scale.value
  const hit = findShapeAt(x, y)
  if (!hit) return
  emit('shape-select', hit.shape, hit.index)
  if (hit.shape.type === 'textbox') {
    startEdit(hit.shape, hit.index)
  } else if (hit.shape.type === 'table') {
    startCellEdit(hit.shape, hit.index, x, y)
  }
}

function findShapeAt(x: number, y: number) {
  const shapes = props.slide?.shapes || []
  for (let i = shapes.length - 1; i >= 0; i--) {
    const shape = shapes[i]
    if (x >= shape.x && x <= shape.x + shape.w &&
        y >= shape.y && y <= shape.y + shape.h) {
      return { shape, index: i }
    }
  }
  return null
}

function startEdit(shape: SlideShape, index: number) {
  editingCell.value = null
  editingShape.value = JSON.parse(JSON.stringify(shape))
  editingShapeIndex.value = index
  editingText.value = (shape.paragraphs || []).map(p => p.text).join('\n')
  nextTick(() => {
    editorRef.value?.focus()
    editorRef.value?.select()
  })
}

function startCellEdit(shape: SlideShape, shapeIndex: number, x: number, y: number) {
  const cloned: SlideShape = JSON.parse(JSON.stringify(shape))
  const rows = cloned.rows || []
  if (!rows.length) return
  const row = Math.min(rows.length - 1, Math.floor((y - cloned.y) / Math.max(20, cloned.h / rows.length)))
  const colCount = rows[row]?.length || 1
  const col = Math.min(colCount - 1, Math.floor((x - cloned.x) / (cloned.w / colCount)))
  editingShape.value = null
  editingCell.value = { shape: cloned, shapeIndex, row, col }
  editingText.value = rows[row]?.[col]?.text || ''
  nextTick(() => {
    editorRef.value?.focus()
    editorRef.value?.select()
  })
}

function commitEdit() {
  if (editingCell.value && props.slide) {
    const { shape, shapeIndex, row, col } = editingCell.value
    if (shape.rows?.[row]?.[col]) {
      shape.rows[row][col] = { ...shape.rows[row][col], text: editingText.value }
      const updatedShapes = [...props.slide.shapes]
      updatedShapes[shapeIndex] = shape
      emit('update:shapes', updatedShapes)
    }
    editingCell.value = null
    render()
    return
  }
  if (!editingShape.value || !props.slide) return

  const text = editingText.value
  const lines = text.split('\n')
  const originalParas = editingShape.value.paragraphs || []

  // 更新段落文本，保留原格式
  const updatedParas = lines.map((line, i) => {
    const base = originalParas[i] || originalParas[originalParas.length - 1] || {
      fontSize: 14, bold: false, color: '#333333', align: 'left' as const,
    }
    return { ...base, text: line }
  })

  editingShape.value.paragraphs = updatedParas

  // 通知父组件
  const updatedShapes = [...props.slide!.shapes]
  updatedShapes[editingShapeIndex.value] = { ...editingShape.value }
  emit('update:shapes', updatedShapes)

  editingShape.value = null
  editingShapeIndex.value = -1
  render()
}

function cancelEdit() {
  editingShape.value = null
  editingCell.value = null
  editingShapeIndex.value = -1
}

// hover 光标
function handleMouseMove(e: MouseEvent) {
  if (!props.editable || !canvasRef.value) return
  const rect = canvasRef.value.getBoundingClientRect()
  const x = (e.clientX - rect.left) / scale.value
  const y = (e.clientY - rect.top) / scale.value

  let isEditable = false
  if (props.slide) {
    for (const shape of props.slide.shapes) {
      if (x >= shape.x && x <= shape.x + shape.w &&
          y >= shape.y && y <= shape.y + shape.h) {
        isEditable = ['textbox', 'table', 'image'].includes(shape.type)
        break
      }
    }
  }
  canvasRef.value.style.cursor = isEditable ? 'pointer' : 'default'
}

// 监听数据变化重新渲染
watch(() => props.slide, () => {
  nextTick(render)
}, { deep: true })

watch(scale, () => {
  nextTick(render)
})

watch(() => props.selectedShapeId, () => nextTick(render))

onMounted(() => {
  render()
})

defineExpose({ render })
</script>

<style scoped>
.slide-canvas-wrapper {
  position: relative;
  overflow: hidden;
  border-radius: 4px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}

.slide-canvas {
  display: block;
  width: 100%;
  height: 100%;
}

.text-editor-overlay {
  position: absolute;
  z-index: 10;
  pointer-events: auto;
}

.text-editor {
  width: 100%;
  min-height: 100%;
  border: 2px solid #3B7A57;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.95);
  padding: 4px;
  outline: none;
  resize: vertical;
  font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
  line-height: 1.4;
}
</style>
