<template>
  <div ref="previewRoot" class="preview">
    <div v-if="heightPrompt" class="height-prompt"></div>
    <template v-for="(row, i) in rowList">
      <!-- 行 -->
      <div :key="i" :class="['row', `row-${i}`]" :style="getMultipleColStyle(i)">
        <!-- 列 -->
        <template v-for="(col, j) in row.col_list">
          <div :key="j" :class="['col', `col-${i}-${j}`]" :style="computedColStyle(col)">
            <!-- 网格 -->
            <div
              v-for="(grid, k) in col.grid_list"
              :key="`grid-${i}-${j}-${k}`"
              :style="{ gridArea: grid.grid_area, height: grid.height }"
              :class="[!noMoveComponent.includes(grid.type) ? 'grid' : 'no-grid']"
            >
              <template v-for="{ type, cursor, lineClass } in moveLineList">
                <span
                  v-if="!noMoveComponent.includes(grid.type)"
                  :key="`${type}-${i}-${j}-${k}`"
                  class="drag-line"
                  :class="[type, ...lineClass]"
                  :style="{ gridArea: type, cursor }"
                  :data-type="type"
                  @mousedown="dragStart($event, { cursor, type, i, j, k, id: grid.id })"
                ></span>
              </template>
              <component
                :is="previewComponentList[grid.type]"
                :id="grid.id"
                ref="preview"
                :key="`preview-${grid.id}`"
                :courseware-id="coursewareId"
                type="edit"
                :class="[grid.id]"
                :style="{
                  gridArea: 'preview',
                  height: grid.height,
                  overflow: 'auto',
                }"
                @handleHeightChange="handleHeightChange"
              />
            </div>
          </div>
        </template>
      </div>
    </template>
  </div>
</template>

<script>
import { previewComponentList } from '@/views/book/courseware/data/bookType';

export default {
  name: 'PreviewEdit',
  provide() {
    return {
      getDragStatus: () => this.drag.dragging,
      bookInfo: this.bookInfo,
      getPermissionControl: () => this.permissionControl,
    };
  },
  props: {
    rowList: {
      type: Array,
      required: true,
    },
    coursewareId: {
      type: String,
      required: true,
    },
  },
  data() {
    return {
      previewComponentList,
      drag: {
        dragging: false,
        i: -1,
        j: -1,
        k: -1,
        startX: 0,
        startY: 0,
        type: '',
        id: '',
      },
      moveLineList: [
        {
          type: 'top',
          cursor: 'default',
          lineClass: ['drag-line'],
        },
        {
          type: 'left',
          cursor: 'ew-resize',
          lineClass: ['drag-vertical-line'],
        },
        {
          type: 'right',
          cursor: 'ew-resize',
          lineClass: ['drag-vertical-line'],
        },
        {
          type: 'bottom',
          cursor: 'ns-resize',
          lineClass: ['drag-line'],
        },
      ],
      dragElement: null, // 当前拖拽的组件实例
      // 不需要移动的组件
      noMoveComponent: ['divider', 'spacing'],
      bookInfo: {
        theme_color: '',
      },
      resizeObserver: null, // 用于监听高度变化
      heightPrompt: false, // 是否显示高度提示线
      permissionControl: {
        can_answer: false, // 可作答
        can_judge_correct: false, // 可判断对错（客观题）
        can_show_answer: false, // 可查看答案
        can_correct: false, // 可批改
        can_check_correct: false, // 可查看批改
      },
    };
  },
  created() {
    document.addEventListener('mousemove', this.dragMove);
    document.addEventListener('mouseup', this.dragEnd);
  },
  mounted() {
    const element = this.$refs.previewRoot;
    // 监听 courseware 高度变化，获取其高度
    this.resizeObserver = new ResizeObserver(() => {
      const rect = element.getBoundingClientRect();
      this.heightPrompt = rect.height > 1620;
    });
    this.resizeObserver.observe(element);
  },
  beforeDestroy() {
    document.removeEventListener('mousemove', this.dragMove);
    document.removeEventListener('mouseup', this.dragEnd);
    if (this.resizeObserver) {
      this.resizeObserver.disconnect();
    }
  },
  methods: {
    handleHeightChange(id, newHeight) {
      this.$emit('handleHeightChange', id, newHeight);
    },
    getMultipleColStyle(i) {
      let row = this.rowList[i];
      let col = row.col_list;
      if (col.length <= 1) {
        return {
          gridTemplateColumns: '100fr',
        };
      }
      let gridTemplateColumns = row.width_list.join(' ');

      return {
        gridAutoFlow: 'column',
        gridTemplateColumns,
        gridTemplateRows: 'auto',
      };
    },
    /**
     * 分割整数为多个 1的倍数
     * @param {number} num
     * @param {number} parts
     */
    splitInteger(num, parts) {
      let base = Math.floor(num / parts);
      let arr = Array(parts).fill(base);
      let remainder = num - base * parts;
      for (let i = 0; remainder > 0; i = (i + 1) % parts) {
        arr[i] += 1;
        remainder -= 1;
      }
      return arr;
    },
    /**
     * 计算列样式
     * @param {Object} col 列对象
     */
    computedColStyle(col) {
      const grid = col.grid_list;

      // 单次分组：后续 areas / columns / rows 统一复用 rowGroups。
      const rowGroups = new Map();
      grid.forEach((item) => {
        if (!rowGroups.has(item.row)) {
          rowGroups.set(item.row, []);
        }
        rowGroups.get(item.row).push(item);
      });

      let maxCol = 0; // 最大列数
      let curMaxRow = 0; // 当前数量最大 row 的值
      rowGroups.forEach((items, row) => {
        if (items.length > maxCol) {
          maxCol = items.length;
          curMaxRow = row;
        }
      });

      const sortedRows = Array.from(rowGroups.keys()).sort((a, b) => a - b);

      // 计算 grid_template_areas
      let gridTemplateAreas = '';
      sortedRows.forEach((row) => {
        const rowItems = rowGroups.get(row) || [];
        const needNum = maxCol - rowItems.length; // 需要补齐的数量
        const splitArr = rowItems.length > 1 ? this.splitInteger(needNum, rowItems.length) : [];

        const areaList = rowItems.map(({ grid_area }, index) => {
          if (curMaxRow === row) {
            return `${grid_area}`;
          }
          if (rowItems.length === 1) {
            return ` ${grid_area} `.repeat(needNum + 1);
          }
          return splitArr[index] === 0 ? ` ${grid_area} ` : ` ${grid_area} `.repeat(splitArr[index] + 1);
        });

        gridTemplateAreas += `'${areaList.join(' ')}' `;
      });

      // 计算 grid_template_columns
      let gridTemplateColumns = '';
      const maxRowItems = rowGroups.get(curMaxRow) || [];
      maxRowItems.forEach((item) => {
        gridTemplateColumns += `${item.width} `;
      });

      // 计算 grid_template_rows
      let gridTemplateRows = '';
      sortedRows.forEach((row) => {
        const heights = (rowGroups.get(row) || []).map((item) => item.height);
        if (heights.length === 1) {
          gridTemplateRows += `${heights[0]} `;
        } else {
          const isAllAuto = heights.every((item) => item === 'auto'); // 是否全是 auto
          gridTemplateRows += isAllAuto ? 'auto ' : `max(${heights.join(', ')}) `;
        }
      });

      return {
        width: col.width,
        gridTemplateAreas,
        gridTemplateColumns,
        gridTemplateRows,
      };
    },
    /**
     * 拖拽开始
     * @param {MouseEvent} event
     * @param {string} cursor
     * @param {string} type
     * @param {string} id
     * @param {number} i
     * @param {number} j
     * @param {number} k
     */
    dragStart(event, { cursor, type, id, i, j, k }) {
      const dragElement = this.findChildComponentByKey(`preview-${id}`);
      if (!dragElement) return;
      if (cursor === 'default') return; // 无需拖动
      this.dragElement = dragElement;

      const { clientX, clientY } = event;
      this.drag = {
        dragging: true,
        startX: clientX,
        startY: clientY,
        type,
        id,
        i,
        j,
        k,
      };

      document.body.style.cursor = cursor;
    },
    /**
     * 拖拽移动
     * @param {MouseEvent} event
     */
    dragMove(event) {
      if (!this.drag.dragging) return;

      const { clientX, clientY } = event;
      const { i, j, k, id, startX, startY, type } = this.drag;
      const offsetX = clientX - startX;
      const offsetY = clientY - startY;

      let { min_height, min_width } = this.dragElement.data;

      const ROW_WIDTH = 1000;

      this.$emit('computedMoveData', {
        i,
        j,
        k,
        offsetX,
        offsetY,
        type,
        id,
        min_width,
        min_height,
        row_width: ROW_WIDTH,
      });

      this.drag.startX = clientX;
      this.drag.startY = clientY;

      this.$forceUpdate();
    },
    /**
     * 拖拽结束
     */
    dragEnd() {
      this.drag = {
        dragging: false,
        startX: 0,
        startY: 0,
        type: '',
        id: '',
        i: -1,
        j: -1,
        k: -1,
      };

      this.dragElement = null;
      document.body.style.cursor = 'auto';
    },
    // 获取子组件
    findChildComponentByKey(key) {
      return this.$children.find((child) => child.$vnode.key === key);
    },
  },
};
</script>

<style lang="scss" scoped>
.preview {
  position: relative;
  display: flex;
  flex-direction: column;
  row-gap: $component-spacing;
  width: calc($courseware-width + 200px);
  height: 100%;
  min-height: calc(100vh - 240px);
  padding: calc($courseware-top-padding + 15px) 100px calc($courseware-bottom-padding + 15px) 100px;
  margin: 0 auto;
  overflow: hidden;
  background-color: #fff;
  background-repeat: no-repeat;
  border-bottom-right-radius: 12px;
  border-bottom-left-radius: 12px;

  .height-prompt {
    position: absolute;
    top: 1620px;
    left: -200px;
    width: 1400px;
    border-top: 2px dashed #903ff8;
  }

  .row {
    display: grid;
    gap: $component-spacing;

    .col {
      display: grid;
      gap: $component-spacing;
      align-items: flex-start;

      .grid {
        display: grid;
        grid-template:
          'top top top' 3px
          'left preview right' 1fr
          'bottom bottom bottom' 3px / 3px 1fr 3px;
      }

      .drag-line {
        z-index: 2;
        width: 100%;
        height: 6px;
        cursor: ns-resize;
        background: linear-gradient(to bottom, transparent, transparent 40%, #e5e6eb 40%, #e5e6eb 60%, transparent 60%);

        &.bottom {
          background: linear-gradient(
            to bottom,
            transparent,
            transparent 20%,
            #e5e6eb 20%,
            #e5e6eb 50%,
            transparent 50%
          );
        }
      }

      .drag-vertical-line {
        z-index: 2;
        width: 6px;
        height: 100%;
        cursor: ew-resize;
        background: linear-gradient(to right, transparent, transparent 40%, #e5e6eb 40%, #e5e6eb 60%, transparent 60%);

        &.left {
          background: linear-gradient(to left, transparent, transparent 60%, #e5e6eb 60%, #e5e6eb);
        }
      }
    }
  }
}
</style>
