<template>
  <div class="ocr-labeling-detail-container">
    <!-- 顶部操作栏：仅保留图片切换、缩放、重置视图 -->
    <div class="ocr-labeling-header">
      <div class="header-left">
        <el-button-group>
          <!-- 图片切换 -->
          <el-button icon="el-icon-arrow-left" @click="prevImage" title="上一张" :disabled="imageList.length <= 1"></el-button>
          <el-button icon="el-icon-arrow-right" @click="nextImage" title="下一张" :disabled="imageList.length <= 1"></el-button>
          <!-- 缩放 -->
          <el-button icon="el-icon-zoom-in" @click="zoomIn" title="放大"></el-button>
          <el-button icon="el-icon-zoom-out" @click="zoomOut" title="缩小"></el-button>
          <el-button icon="el-icon-refresh" @click="resetZoom" title="重置视图"></el-button>
        </el-button-group>
      </div>
      <div class="header-right"></div>
    </div>

    <!-- 主体内容区 -->
    <div class="ocr-labeling-main-wrapper">
      <div class="ocr-labeling-main">
        <!-- 左侧OCR标注回显区：复用分类标注的canvas+拖拽/缩放逻辑 -->
        <div class="ocr-labeling-image-area">
          <!-- 图片加载中占位 -->
          <div v-if="imageLoading" class="image-loading">
            <el-skeleton :rows="10" animated />
          </div>
          <div v-else ref="labelContainer" class="image-container"
               @wheel.prevent="handleWheelZoom"
               @mousedown="handleImageMouseDown"
               @mousemove="handleImageMouseMove"
               @mouseup="handleImageMouseUp"
               @mouseleave="handleImageMouseUp">
            <!-- 无效数据标记提示 -->
            <div v-if="currentImage.isInvalidData === '0'" class="invalid-data-tip">无效数据</div>
            <!-- img仅作为加载载体，display:none隐藏，彻底解决双图 -->
            <img
              ref="labelImage"
              :src="currentImage.filePath"
              alt="OCR标注详情图片"
              class="label-image"
              @load="onImageLoad"
              @error="onImageError"
              @mousedown.prevent
            />
          </div>
          <!-- 图片分页&缩放信息 -->
          <div class="image-pagination" v-if="!imageLoading">
            第 {{ currentImageIndex + 1 }} 张/共 {{ imageList.length }} 张 | 缩放比例：{{ Math.round(zoomScale * 100) }}%
          </div>
        </div>

        <!-- 右侧标签栏：仅展示标签及下属OCR识别结果，移除所有编辑功能 -->
        <div class="ocr-labeling-sidebar">
          <div class="sidebar-title">OCR标注信息</div>
          <!-- 标签加载状态 -->
          <div v-if="labelLoading" class="label-loading">
            <el-skeleton :rows="5" animated />
          </div>
          <!-- 标签列表（树状展示：标签+OCR识别结果子项） -->
          <div v-else class="sidebar-content">
            <div class="label-group" v-for="(group, index) in labelGroups" :key="group.id || index">
              <div
                class="label-group-header"
                @click="group.expanded = !group.expanded"
              >
              <i
                class="el-icon"
                :class="group.expanded ? 'el-icon-arrow-down' : 'el-icon-arrow-right'"
                style="transition: transform 0.2s ease;"
              ></i>
              <span>{{ group.name }} ({{ getOcrItemCount(group) }})</span>
            </div>
              <div class="label-list" v-if="group.expanded">
                <div
                  class="label-item"
                  v-for="(label, labelIndex) in group.labels"
                  :key="label.id || labelIndex"
                >
                  <span class="label-name">#{{ labelIndex + 1 }} {{ label.name }}</span>
                  <!-- 该标签下的OCR识别结果子项：仅展示 -->
                  <div class="ocr-result-list" v-if="label.ocrResults && label.ocrResults.length">
                    <div
                      class="ocr-result-item"
                      v-for="(ocr, ocrIndex) in label.ocrResults"
                      :key="ocr.shapeId"
                    >
                      <span class="ocr-result-text">{{ ocr.textContent }}</span>
                      <span class="shape-type">
                        {{ ocr.shapeType === 'rect' ? '矩形标注' : '四边形标注' }}
                      </span>
                    </div>
                  </div>
                </div>
              </div>
            </div>
            <!-- 无标签提示 -->
            <div v-if="labelGroups.length === 0" class="no-label-data">
              <el-empty description="暂无OCR标注数据"></el-empty>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { decryptWithSM4 } from "@/utils/sm";
import {
  getLabelingTaskLabelTreeAPI,
  getLabelPicturesDetailAPI
} from '@/api/data/labeling';

export default {
  name: 'OcrLabelingDetail',
  data() {
    return {
      // OCR标注样式配置（仅展示）
      ocrDrawConfig: {
        baseLineWidth: 3,
        minLineWidth: 1,
        maxLineWidth: 6,
        rectColor: '#1890ff',
        quadColor: '#1890ff',
        selectedColor: '#ff4d4f',
        shapeFillOpacity: 0.2,
      },
      // 图片相关（从详情接口获取）
      imageList: [],
      currentImageIndex: 0,
      currentImage: {},
      imageLoading: false,
      imageLoaded: false,
      // 标签相关（仅展示）
      labelGroups: [],
      labelLoading: false,
      // 缩放/拖拽核心参数：完全复用分类标注的配置
      zoomScale: 1.0,
      imgOffsetX: 0,
      imgOffsetY: 0,
      dragState: {
        isDragging: false,
        startX: 0,
        startY: 0,
        startImgX: 0,
        startImgY: 0
      },
      // 路由参数
      labelingTaskId: this.convertToNumber(decryptWithSM4(this.$route.query.labelingTaskId || '')),
      labelingTemplate: decryptWithSM4(this.$route.query.labelingTemplate || '')
    }
  },
  mounted() {
    // 并行加载标签树 + 详情图片列表
    Promise.all([
      this.loadLabelTreeData(),
      this.loadImageDetailList()
    ]).then(() => {
      if (this.imageList.length > 0) {
        this.currentImage = this.imageList[0];
      }
    });
  },
  beforeDestroy() {
    // 移除画布监听，清理DOM
    const container = this.$refs.labelContainer;
    if (container) {
      const canvas = container.querySelector('.image-canvas');
      canvas && canvas.remove();
    }
  },
  methods: {
    // 工具方法：转换为数字
    convertToNumber(value) {
      if (!value || value === '' || value === 'undefined' || value === 'null') return '';
      const num = Number(value);
      return isNaN(num) ? '' : num;
    },

    // 工具方法：16进制转RGBA
    hexToRgba(hex, opacity) {
      const cleanHex = hex.replace('#', '');
      const fullHex = cleanHex.length === 3 ? cleanHex.split('').map(c => c + c).join('') : cleanHex;
      const r = parseInt(fullHex.substring(0, 2), 16);
      const g = parseInt(fullHex.substring(2, 4), 16);
      const b = parseInt(fullHex.substring(4, 6), 16);
      return `rgba(${r}, ${g}, ${b}, ${opacity})`;
    },

    // 计算标签下OCR结果总数
    getOcrItemCount(group) {
      return group.labels.reduce((total, label) => {
        return total + (label.ocrResults ? label.ocrResults.length : 0);
      }, 0);
    },

    // 加载标签树数据（仅展示，初始化OCR结果空数组）
    async loadLabelTreeData() {
      try {
        this.labelLoading = true;
        if (!this.labelingTaskId) return;
        const res = await getLabelingTaskLabelTreeAPI({ labelingTaskId: this.labelingTaskId });
        if (res.code === 200 && res.data) {
          this.labelGroups = res.data.map(group => ({
            ...group,
            expanded: true,
            labels: (group.labels || []).map(label => ({
              ...label,
              ocrResults: []
            }))
          }));
        } else {
          this.$message.warning('标签数据加载失败：' + (res.msg || '接口返回异常'));
        }
      } catch (error) {
        console.error('加载标签树失败：', error);
        this.$message.error('标签数据加载失败，请刷新重试');
      } finally {
        this.labelLoading = false;
      }
    },

    // 加载OCR标注详情图片列表（核心：调用详情接口，按图片分组数据）
    async loadImageDetailList() {
      try {
        this.imageLoading = true;
        if (!this.labelingTaskId) return;
        const res = await getLabelPicturesDetailAPI({ labelingTaskId: this.labelingTaskId });
        if (res.code === 200 && res.data && Array.isArray(res.data)) {
          const imageMap = {};
          res.data.forEach(item => {
            const key = item.labelingSampleId;
            if (!imageMap[key]) {
              imageMap[key] = {
                id: item.labelingSampleId,
                datasetSampleId: item.datasetSampleId || '',
                filePath: item.filePath,
                isInvalidData: item.isInvalidData || '1',
                shapes: [],
                ocrResults: []
              };
            }

            // 解析标注坐标
            let coordinateData = {};
            try {
              coordinateData = JSON.parse(item.labelingCoordinate || '{}');
            } catch (e) {
              console.warn('解析OCR标注坐标失败：', e);
              coordinateData = {};
            }

            // 生成唯一形状ID
            const shapeId = `shape_${item.labelingSampleId}_${item.id || Date.now()}`;
            // 转换boxType为前端标注类型
            const shapeType = item.boxType === '1' ? 'rect' : 'quad';

            // 构造OCR标注形状数据
            const shape = {
              id: shapeId,
              type: shapeType,
              x: Number(coordinateData.x) || 0,
              y: Number(coordinateData.y) || 0,
              width: Number(coordinateData.width) || 0,
              height: Number(coordinateData.height) || 0,
              points: coordinateData.points || [],
              color: item.labelingColor || this.ocrDrawConfig[`${shapeType}Color`],
              labelId: item.labelId,
              textContent: item.textContent || ''
            };

            // 添加形状和OCR结果
            imageMap[key].shapes.push(shape);
            imageMap[key].ocrResults.push({
              shapeId,
              shapeType,
              labelId: item.labelId,
              textContent: item.textContent || ''
            });
          });

          this.imageList = Object.values(imageMap);
          if (this.imageList.length > 0) {
            this.currentImage = this.imageList[0];
            this.bindOcrResultToLabel(this.currentImage.ocrResults);
          } else {
            this.$message.info('暂无OCR标注详情图片');
          }
        } else {
          this.$message.warning('图片详情数据加载失败：' + (res.msg || '接口返回异常'));
        }
      } catch (error) {
        console.error('加载OCR标注详情失败：', error);
        this.$message.error('图片数据加载失败，请刷新重试');
      } finally {
        this.imageLoading = false;
      }
    },

    // 绑定OCR结果到对应标签（解决类型不匹配问题）
    bindOcrResultToLabel(ocrResults = []) {
      if (!ocrResults.length) return;
      // 清空原有OCR结果
      this.labelGroups.forEach(group => {
        group.labels.forEach(label => {
          label.ocrResults = [];
        });
      });
      // 重新绑定：字符串labelId转数字，匹配标签树的数字id
      ocrResults.forEach(ocr => {
        const ocrLabelId = Number(ocr.labelId);
        for (const group of this.labelGroups) {
          const label = group.labels.find(l => l.id === ocrLabelId);
          if (label) {
            label.ocrResults.push({
              shapeId: ocr.shapeId,
              shapeType: ocr.shapeType,
              textContent: ocr.textContent
            });
            break;
          }
        }
      });
    },

    // 图片加载完成：复用分类标注逻辑，初始化画布+缩放+绑定OCR
    onImageLoad() {
      this.imageLoaded = true;
      this.calcFitScale();
      this.imgOffsetX = 0;
      this.imgOffsetY = 0;
      this.initImageCanvas(); // 初始化画布（图片+标注框）
    },

    // 图片加载失败
    onImageError() {
      this.$message.error(`图片加载失败：${this.currentImage.filePath}`);
      this.imageLoaded = false;
    },

    // 计算图片适配缩放比例：完全复用分类标注逻辑
    calcFitScale() {
      const container = this.$refs.labelContainer;
      const image = this.$refs.labelImage;
      if (!container || !image) return;
      const containerW = container.clientWidth;
      const containerH = container.clientHeight;
      const imgW = image.naturalWidth;
      const imgH = image.naturalHeight;
      const scaleW = containerW / imgW;
      const scaleH = containerH / imgH;
      this.zoomScale = Math.min(scaleW, scaleH);
      this.zoomScale = Math.max(0.1, Math.min(2.0, this.zoomScale));
    },

    // 初始化画布：复用分类标注逻辑，创建画布并首次绘制
    initImageCanvas() {
      const container = this.$refs.labelContainer;
      const image = this.$refs.labelImage;
      if (!container || !image) return;

      // 移除旧画布
      const oldCanvas = container.querySelector('.image-canvas');
      if (oldCanvas) oldCanvas.remove();

      // 创建新画布：和分类标注样式、层级一致
      const canvas = document.createElement('canvas');
      canvas.className = 'image-canvas';
      canvas.style.cssText = 'position:absolute;top:0;left:0;pointer-events:none;z-index:10;width:100%;height:100%;';
      canvas.width = container.offsetWidth;
      canvas.height = container.offsetHeight;
      container.appendChild(canvas);

      // 重绘画布（图片+OCR标注框）
      this.updateImageCanvas();
    },

    // 重绘画布：核心！融合分类标注的「图片+缩放+拖拽」+ OCR的「标注框绘制」
    updateImageCanvas() {
      const container = this.$refs.labelContainer;
      const image = this.$refs.labelImage;
      const canvas = container.querySelector('.image-canvas');
      if (!container || !image || !canvas || !this.imageLoaded) return;

      const ctx = canvas.getContext('2d');
      // 重置画布大小
      canvas.width = container.offsetWidth;
      canvas.height = container.offsetHeight;
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.save();

      // 核心：复用分类标注的「居中+缩放+拖拽偏移」绘制逻辑
      ctx.translate(
        container.offsetWidth / 2 + this.imgOffsetX,
        container.offsetHeight / 2 + this.imgOffsetY
      );
      ctx.scale(this.zoomScale, this.zoomScale);
      ctx.translate(-image.naturalWidth / 2, -image.naturalHeight / 2);

      // 1. 绘制图片（唯一图片层，彻底解决双图）
      ctx.drawImage(image, 0, 0, image.naturalWidth, image.naturalHeight);

      // 2. 绘制OCR标注框：融合原有OCR绘制逻辑，适配画布缩放
      this.drawOcrShapes(ctx, image);

      ctx.restore();
    },

    // 绘制OCR标注框：单独封装，适配画布的缩放/偏移逻辑
    drawOcrShapes(ctx, image) {
      if (!this.currentImage || !this.currentImage.shapes || this.currentImage.shapes.length === 0) return;

      const dynamicLineWidth = this.getDynamicLineWidth();
      this.currentImage.shapes.forEach(s => {
        const strokeColor = s.color || this.ocrDrawConfig[`${s.type}Color`];
        const fillColor = this.hexToRgba(strokeColor, this.ocrDrawConfig.shapeFillOpacity);

        ctx.lineWidth = dynamicLineWidth;
        ctx.strokeStyle = strokeColor;
        ctx.fillStyle = fillColor;

        // 绘制矩形标注
        if (s.type === 'rect') {
          ctx.strokeRect(s.x, s.y, s.width, s.height);
          ctx.fillRect(s.x, s.y, s.width, s.height);
        }
        // 绘制四边形标注
        else if (s.type === 'quad' && s.points.length >= 4) {
          ctx.beginPath();
          s.points.forEach((point, idx) => {
            const x = Number(point.x) || Number(point[0]) || 0;
            const y = Number(point.y) || Number(point[1]) || 0;
            idx === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
          });
          ctx.closePath();
          ctx.fill();
          ctx.stroke();
        }
      });
    },

    // 动态计算线条宽度（适配缩放）
    getDynamicLineWidth() {
      const { baseLineWidth, minLineWidth, maxLineWidth } = this.ocrDrawConfig;
      const dynamicWidth = baseLineWidth / this.zoomScale;
      return Math.max(minLineWidth, Math.min(maxLineWidth, dynamicWidth));
    },

    // ########## 拖拽/缩放/图片切换：完全复用分类标注的逻辑 ##########
    // 滚轮缩放
    handleWheelZoom(e) {
      e.preventDefault();
      const delta = e.deltaY > 0 ? -0.1 : 0.1;
      this.zoomScale = Math.max(0.1, Math.min(2.0, this.zoomScale + delta));
      this.updateImageCanvas();
    },

    // 鼠标按下：开启拖拽
    handleImageMouseDown(e) {
      e.preventDefault();
      this.dragState = {
        isDragging: true,
        startX: e.clientX,
        startY: e.clientY,
        startImgX: this.imgOffsetX,
        startImgY: this.imgOffsetY
      };
      this.$refs.labelContainer.style.cursor = 'move';
    },

    // 鼠标移动：处理拖拽
    handleImageMouseMove(e) {
      e.preventDefault();
      if (!this.dragState.isDragging) return;
      const moveX = e.clientX - this.dragState.startX;
      const moveY = e.clientY - this.dragState.startY;
      this.imgOffsetX = this.dragState.startImgX + moveX;
      this.imgOffsetY = this.dragState.startImgY + moveY;
      this.updateImageCanvas();
    },

    // 鼠标松开/离开：结束拖拽
    handleImageMouseUp() {
      if (this.dragState.isDragging) {
        this.dragState.isDragging = false;
        this.$refs.labelContainer.style.cursor = 'default';
      }
    },

    // 放大/缩小/重置
    zoomIn() {
      this.zoomScale = Math.min(2.0, this.zoomScale + 0.1);
      this.updateImageCanvas();
    },
    zoomOut() {
      this.zoomScale = Math.max(0.1, this.zoomScale - 0.1);
      this.updateImageCanvas();
    },
    resetZoom() {
      this.calcFitScale();
      this.imgOffsetX = 0;
      this.imgOffsetY = 0;
      this.updateImageCanvas();
    },

    // 上一张/下一张/切换图片
    prevImage() {
      if (this.currentImageIndex > 0) {
        this.switchImage(this.currentImageIndex - 1);
      } else {
        this.$message.info('已是第一张！');
      }
    },
    nextImage() {
      if (this.currentImageIndex < this.imageList.length - 1) {
        this.switchImage(this.currentImageIndex + 1);
      } else {
        this.$message.info('已是最后一张！');
      }
    },
    // 切换图片：完全复用分类标注逻辑，重置状态+重新绘制
    switchImage(index) {
      this.currentImageIndex = index;
      this.currentImage = this.imageList[index];
      this.imageLoaded = false;
      this.imgOffsetX = 0;
      this.imgOffsetY = 0;
      this.zoomScale = 1.0;
      // 绑定当前图片OCR结果
      this.bindOcrResultToLabel(this.currentImage.ocrResults);
      // 重新初始化画布
      this.$nextTick(() => {
        if (this.$refs.labelImage) {
          this.calcFitScale();
          this.initImageCanvas();
        }
      });
    }
  }
}
</script>

<style scoped lang="scss">
// 全局容器
.ocr-labeling-detail-container {
  width: 100%;
  height: 90vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background-color: #f5f7fa;
  box-sizing: border-box;
}

// 顶部操作栏
.ocr-labeling-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 20px;
  background-color: #fff;
  border-bottom: 1px solid #e6e6e6;
  flex-shrink: 0;
  z-index: 100;

  .header-left {
    display: flex;
    align-items: center;
  }
}

// 主体内容外层
.ocr-labeling-main-wrapper {
  height: 100%;
  overflow: hidden;
  padding: 15px;
}

// 主体内容区
.ocr-labeling-main {
  display: flex;
  gap: 20px;
  height: 100%;
  min-height: 0;

  // 左侧OCR标注回显区：复用分类标注的样式，隐藏img
  .ocr-labeling-image-area {
    flex: 1;
    background-color: #fff;
    border-radius: 4px;
    padding: 15px;
    display: flex;
    flex-direction: column;

    .image-loading {
      flex: 1;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 20px;
    }

    .image-container {
      flex: 1;
      position: relative;
      overflow: hidden;
      border: 1px solid #e6e6e6;
      border-radius: 4px;
      min-height: 400px;
      cursor: default;
      display: flex;
      align-items: center;
      justify-content: center;
      background-color: #f0f0f0;
      padding: 0;
      box-sizing: border-box;

      // 无效数据提示
      .invalid-data-tip {
        position: absolute;
        top: 10px;
        left: 10px;
        z-index: 999;
        background-color: rgba(255, 255, 255, 0.8);
        color: #ff4d4f;
        padding: 8px 16px;
        font-size: 16px;
        font-weight: 600;
        border-radius: 6px;
        pointer-events: none;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
        min-width: 120px;
        text-align: center;
      }

      // 核心：img仅作为加载载体，隐藏！彻底解决双图
      .label-image {
        display: none;
      }
    }

    .image-pagination {
      margin-top: 10px;
      font-size: 14px;
      color: #666;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
  }

  // 右侧标签栏：保留原有样式，无修改
  .ocr-labeling-sidebar {
    width: 320px;
    background-color: #fff;
    border-radius: 4px;
    padding: 15px;
    display: flex;
    flex-direction: column;
    flex-shrink: 0;
    border: 1px solid #e6e6e6;

    .sidebar-title {
      font-size: 16px;
      font-weight: 600;
      margin-bottom: 15px;
      padding-bottom: 8px;
      border-bottom: 1px solid #e6e6e6;
    }

    .label-loading {
      padding: 20px 0;
    }

    .sidebar-content {
      flex: 1;
      overflow-y: auto;
      max-height: calc(100vh - 120px);

      .label-group {
        margin-bottom: 15px;

        .label-group-header {
          display: flex;
          align-items: center;
          justify-content: center; // 左对齐，符合常规标签树布局
          padding: 8px 10px;
          background-color: #f5f7fa;
          border-radius: 4px;
          margin-bottom: 8px;
          cursor: pointer; // 悬浮指针，提示可点击
          transition: background-color 0.2s ease; // 悬浮过渡

          &:hover {
            background-color: #e6f7ff; // 悬浮高亮，提升体验
          }

          .el-icon {
            margin-right: 8px;
            font-size: 12px;
            line-height: 1; // 垂直居中
          }
        }

        .label-list {
          padding-left: 10px;

          .label-item {
            display: flex;
            flex-direction: column;
            padding: 8px 10px;
            margin-bottom: 5px;
            border-radius: 4px;
            background-color: #f5f7fa;
            cursor: default;

            .label-name {
              font-size: 14px;
              color: #333;
              margin-bottom: 8px;
            }

            .ocr-result-list {
              padding-left: 15px;
              border-left: 2px solid #e6e6e6;

              .ocr-result-item {
                display: flex;
                justify-content: space-between;
                align-items: center;
                padding: 6px 0;
                font-size: 13px;
                color: #666;
                border-bottom: 1px dashed #f0f0f0;

                &:last-child {
                  border-bottom: none;
                }

                .ocr-result-text {
                  flex: 1;
                  white-space: nowrap;
                  overflow: hidden;
                  text-overflow: ellipsis;
                }

                .shape-type {
                  font-size: 12px;
                  color: #1890ff;
                  margin-left: 8px;
                }
              }
            }
          }
        }
      }

      .no-label-data {
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 20px 0;
      }
    }
  }
}

// 滚动条样式
::v-deep .el-scrollbar__bar {
  width: 6px;
  height: 6px;
}
::v-deep .el-scrollbar__thumb {
  background-color: #ccc;
  border-radius: 3px;
}
</style>
