<template>
  <div class="exercise">
    <div class="list">
      <el-button icon="el-icon-back" @click="back">返回练习管理</el-button>
      <div class="list-title">
        <el-input
          v-if="isEditExercise"
          v-model="exercise.name"
          @keyup.enter.native="UpdateExerciseName"
          @blur="UpdateExerciseName"
        />
        <span v-else class="nowrap-ellipsis">{{ exercise.name }}</span>
        <SvgIcon icon-class="edit" class-name="edit" @click="editExercise" />
      </div>
      <div class="exercise-list">
        <ul>
          <draggable v-model="index_list" animation="300" @end="moveQuestion" @start="handleStart">
            <transition-group>
              <li v-for="(item, i) in index_list" :key="i" :class="['exercise-item', { active: i === curIndex }]">
                <SvgIcon icon-class="child" :size="20" />
                <span class="item-name nowrap-ellipsis" @click="selectExerciseItem(i)">
                  {{ i + 1 }}. {{ exerciseNames[item.type] }}
                </span>
                <SvgIcon icon-class="copy" class="pointer" :size="10" @click="copy(i)" />
              </li>
            </transition-group>
          </draggable>
        </ul>
      </div>
      <div class="list-operate">
        <el-button type="primary" @click="oneClickImport">一键导入</el-button>
        <el-button type="primary" @click="showSelectQuestionType">新建</el-button>
      </div>
    </div>

    <CreateMain
      ref="createMain"
      :cur-index="curIndex"
      :index-list="index_list"
      :loading="loading"
      @selectExerciseItem="selectExerciseItem"
      @setPreview="setPreview"
      @deleteQuestion="deleteQuestion"
    />

    <div class="preview" :style="{ height: preview ? 'calc(50vh - 32px)' : 'auto' }">
      <div class="preview-header">
        <span class="quick-preview">快捷预览：</span>
        <div class="preview-right">
          <template v-if="preview">
            <span class="preview-button plain" @click="refreshPreviewData">
              <SvgIcon icon-class="loop" size="14" /><span>刷新</span>
            </span>
            <span class="preview-button" @click="fullPreview">
              <SvgIcon icon-class="eye" /> <span>完整预览</span>
            </span>
            <span class="preview-button plain" @click="setPreview"><SvgIcon icon-class="close" />关闭预览</span>
          </template>
          <template v-else>
            <span class="preview-button" @click="setPreview"><SvgIcon icon-class="eye" />预览</span>
          </template>
        </div>
      </div>
      <div class="preview-content">
        <component :is="curPreviewPage" v-if="preview" :data="previewData" :child-preview-data="childPreviewData" />
      </div>
    </div>

    <SelectQuestionType :visible.sync="visible" @addQuestionToExercise="addQuestionToExercise" />
    <ImportPage :visible.sync="importVisible" @oneKeyImport="oneKeyImport" />
  </div>
</template>

<script>
import { exerciseNames, questionDataList, analysisOneKeyImportData } from '../data/questionType';
import {
  AddQuestionToExercise,
  DeleteQuestion,
  GetExerciseQuestionIndexList,
  GetQuestionInfo,
  GetExerciseInfo,
  UpdateExercise,
  MoveQuestion,
} from '@/api/exercise';

import CreateMain from './components/create.vue';
import PreviewQuestionTypeMixin from '../data/PreviewQuestionTypeMixin';
import ImportPage from './components/common/ImportPage.vue';
import SelectQuestionType from './components/common/SelectQuestionType.vue';
import draggable from 'vuedraggable';

export default {
  name: 'CreateExercise',
  components: {
    CreateMain,
    SelectQuestionType,
    draggable,
    ImportPage,
  },
  mixins: [PreviewQuestionTypeMixin],
  provide() {
    return {
      updateCurQuestionType: this.updateCurQuestionType,
      exercise_id: this.exercise_id,
      refreshPreviewData: this.refreshPreviewData,
      updateLoading: (loading) => {
        this.loading = loading;
      },
    };
  },
  data() {
    const { id, back_url } = this.$route.query;

    return {
      loading: false, // 题目加载中
      exercise_id: id, // 练习id
      exercise: { name: '' }, // 练习信息
      isEditExercise: false, // 是否编辑练习
      moveQuestionData: {}, // 移动题目数据
      isMoveQuestion: false, // 是否移动题目
      curIndex: 0, // 当前练习索引
      index_list: [], // 练习列表
      exerciseNames, // 练习名称
      preview: false, // 预览显示
      previewData: {}, // 预览数据
      back_url: back_url || '/personal_question', // 返回地址
      visible: false, // 选择题目类型弹窗
      importVisible: false, // 一键导入弹窗
    };
  },
  computed: {
    curPreviewPage() {
      if (this.index_list.length === 0) return '';
      return this.previewComponents[this.index_list[this.curIndex].type];
    },
  },
  watch: {
    curIndex() {
      if (this.index_list.length === 0 || this.isMoveQuestion) return;
      this.getQuestionInfo();
    },
  },
  created() {
    this.getExerciseQuestionIndexList(true);
    this.getExerciseInfo();
  },
  methods: {
    // 返回练习管理
    back() {
      this.$router.push(this.back_url);
    },
    /**
     * 添加题目到练习
     * @param {string} type 题目类型
     * @param {string} additional_type 附加类型
     * @param {string} content 题目内容
     */
    addQuestionToExercise(type, additional_type, content = '') {
      this.$refs.createMain.saveQuestion();
      AddQuestionToExercise({
        exercise_id: this.exercise_id,
        type,
        additional_type,
        content,
      })
        .then(() => {
          this.getExerciseQuestionIndexList(false, true);
        })
        .catch(() => {
          this.$message.error('添加失败');
        });
    },
    showSelectQuestionType() {
      this.visible = true;
    },
    oneClickImport() {
      this.importVisible = true;
    },
    async oneKeyImport(text) {
      let dataList = await analysisOneKeyImportData(text.split(/\n\s*\n+/));
      this.importVisible = false;
      const loading = this.$loading({
        text: '正在导入题目，请稍等...',
      });
      // 一键导入题目，按 dataList 顺序添加题目，等待第一个题目添加成功后，再添加第二个题目
      dataList
        .reduce((promise, { type, additional_type, content }) => {
          return promise.then(() => {
            return AddQuestionToExercise({
              exercise_id: this.exercise_id,
              type,
              additional_type,
              content,
            });
          });
        }, Promise.resolve())
        .then(() => {
          this.getExerciseQuestionIndexList(false, true);
          loading.close();
        });
    },
    // 创建默认题目
    createDefaultQuestion() {
      this.addQuestionToExercise('select', 'single', questionDataList['select']);
    },
    /**
     * 获取练习题目索引列表
     * @param {boolean} init 是否初始化
     * @param {boolean} isAdd 是否添加题目
     */
    getExerciseQuestionIndexList(init = false, isAdd = false) {
      GetExerciseQuestionIndexList({ exercise_id: this.exercise_id })
        .then(({ index_list }) => {
          this.index_list = index_list;

          if (isAdd) {
            this.curIndex = this.index_list.length - 1;
          }
          if (!init) return;
          this.getQuestionInfo();
        })
        .catch((err) => {
          console.log(err);
        });
    },
    getExerciseInfo() {
      GetExerciseInfo({ exercise_id: this.exercise_id }).then(({ exercise }) => {
        this.exercise = exercise;
      });
    },
    editExercise() {
      if (this.isEditExercise) {
        return this.UpdateExerciseName();
      }
      this.isEditExercise = !this.isEditExercise;
    },
    UpdateExerciseName() {
      UpdateExercise(this.exercise)
        .then(() => {
          this.isEditExercise = false;
        })
        .catch(() => {
          this.$message.error('修改失败');
        });
    },
    /**
     * 获取题目信息
     */
    getQuestionInfo() {
      if (this.index_list.length === 0) return;
      this.$refs.createMain?.clearSaveDate();
      this.loading = true;
      let curId = this.index_list[this.curIndex].id;
      GetQuestionInfo({ question_id: this.index_list[this.curIndex].id })
        .then(({ question, file_list }) => {
          if (curId !== this.index_list[this.curIndex].id) return;
          this.$refs.createMain.resetSaveDate();
          if (!question.content) return;
          // 将题目文件id列表添加到题目内容中
          let file_id_list = file_list.map(({ file_id }) => file_id);
          let content = JSON.parse(question.content);
          content.file_id_list = file_id_list;

          this.$nextTick(() => {
            this.$refs.createMain.$refs.exercise?.[0].setQuestion(content);
            this.refreshPreviewData();
          });
        })
        .catch(() => {});
    },
    /**
     * 复制练习
     * @param {Number} index 练习索引
     */
    copy(index) {
      this.$confirm('是否复制当前题目', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          GetQuestionInfo({ question_id: this.index_list[index].id }).then(
            ({ question: { type, additional_type, content } }) => {
              this.addQuestionToExercise(type, additional_type, content);
            },
          );
        })
        .catch(() => {});
    },
    /**
     * 选择练习
     * @param {number} index 练习索引
     */
    selectExerciseItem(index) {
      if (index < 0 || index > this.index_list.length - 1) return;
      this.$refs.createMain?.clearSaveDate();
      this.$refs.createMain.saveQuestion();
      this.curIndex = index;
    },
    // 刷新预览数据
    refreshPreviewData() {
      this.previewData = this.$refs.createMain.$refs.exercise?.[0].data || {};
      this.childPreviewData = this.$refs.createMain.$refs.exercise?.[0].childPreviewData || [];
    },
    // 完整预览
    fullPreview() {
      window.open(
        this.$router.resolve({
          path: '/answer',
          query: {
            id: this.exercise_id,
            type: 'show',
            question_index: this.curIndex,
            back_url: 'not-return',
          },
        }).href,
        '_blank',
      );
    },
    // 预览
    setPreview() {
      this.preview = !this.preview;
      this.refreshPreviewData();
    },
    // 删除练习
    deleteQuestion() {
      DeleteQuestion({ question_id: this.index_list[this.curIndex].id })
        .then(() => {
          let index = this.curIndex - 1;
          this.curIndex = Math.max(0, index);
          // 当删除的题目是第一题时，需要重新获取题目信息，因为 curIndex 没有变化
          this.getExerciseQuestionIndexList(index < 0);
          this.$message.success('删除成功');
        })
        .catch(() => {
          this.$message.error('删除失败');
        });
    },
    /**
     * 修改当前题目类型
     * @param {array} arr
     */
    updateCurQuestionType(arr) {
      let type = arr[arr.length - 1];
      this.index_list[this.curIndex].type = type;
    },
    /**
     * 移动题目
     * @param {object} param 移动参数
     * @param {number} param.newIndex 移动后的索引
     * @param {number} param.oldIndex 移动前的索引
     */
    moveQuestion({ newIndex, oldIndex }) {
      if (newIndex === oldIndex) {
        this.isMoveQuestion = false;
        this.moveQuestionData = {};
        return;
      }
      let isMoveCur = newIndex === this.curIndex || oldIndex === this.curIndex; // 是否移动当前题目
      let isEffectCurIndex = newIndex < this.curIndex !== oldIndex < this.curIndex; // 是否影响当前题目索引
      if (isMoveCur) {
        this.curIndex = this.curIndex === newIndex ? oldIndex : newIndex;
      } else if (isEffectCurIndex) {
        this.curIndex = newIndex < this.curIndex ? this.curIndex + 1 : this.curIndex - 1;
      }
      this.isMoveQuestion = false;
      if (isMoveCur || isEffectCurIndex) {
        this.$nextTick(() => {
          this.$refs.createMain.$refs.exercise?.[0].setQuestion(this.moveQuestionData);
          this.moveQuestionData = {};
          this.refreshPreviewData();
          this.$refs.createMain.resetSaveDate();
        });
      }

      let order = newIndex > oldIndex;
      MoveQuestion({
        question_id: this.index_list[newIndex].id,
        dest_question_id: this.index_list[order ? newIndex - 1 : newIndex + 1].id,
        dest_position: order ? 1 : 0,
      })
        .then(() => {
          this.$message.success('移动成功');
          let curI = this.curIndex;
          // 移动题目后，单独更新当前题目的题号
          GetQuestionInfo({ question_id: this.index_list[this.curIndex].id })
            .then(({ question }) => {
              if (!question) return;
              if (curI !== this.curIndex) return;
              this.$refs.createMain.$refs.exercise?.[0].setQuestionNumber(question.question_number);
            })
            .catch(() => {});
        })
        .catch(() => {
          this.$message.error('移动失败');
        });
    },
    /**
     * 开始移动题目
     * @param {object} param 移动参数
     * @param {number} param.newIndex 移动后的索引
     * @param {number} param.oldIndex 移动前的索引
     */
    handleStart({ newIndex, oldIndex }) {
      this.$refs.createMain?.clearSaveDate();
      let isMoveCur = newIndex === this.curIndex || oldIndex === this.curIndex; // 是否移动当前题目
      let isEffectCurIndex = newIndex < this.curIndex !== oldIndex < this.curIndex; // 是否影响当前题目索引
      if (isMoveCur || isEffectCurIndex) {
        this.isMoveQuestion = true;
        this.moveQuestionData = this.$refs.createMain.$refs.exercise?.[0].data;
      }
    },
  },
};
</script>

<style lang="scss" scoped>
.exercise {
  display: grid;
  grid-template: 1fr auto / 224px minmax(800px, 1fr);
  height: 100%;

  .list {
    display: flex;
    flex-direction: column;
    grid-area: 1 / 1 / -1;
    row-gap: 16px;
    padding: 16px 8px;
    background-color: #fff;
    border-right: 1px solid $border-color;

    &-title {
      display: flex;
      column-gap: 8px;
      align-items: center;
      font-weight: bold;

      :first-child {
        flex: 1;
      }

      .edit {
        cursor: pointer;
      }
    }

    .exercise-list {
      max-height: calc(100% - 130px);
      overflow-y: auto;

      .exercise-item {
        display: flex;
        column-gap: 8px;
        align-items: center;
        padding: 4px 8px;
        color: #333;
        cursor: pointer;
        border-radius: 2px;

        &.active {
          color: $main-color;
          background-color: #f4f8ff;
        }

        .item-name {
          flex: 1;
        }
      }
    }

    &-operate {
      display: flex;

      > .el-button {
        flex: 1;
      }
    }
  }

  .preview {
    position: relative;
    padding: 16px 24px;
    overflow: auto;
    background-color: #eff1f5;
    border-top: 1px solid $border-color;

    .preview-header {
      display: flex;
      justify-content: space-between;
      width: 100%;

      .quick-preview {
        font-weight: bold;
      }

      .preview-right {
        display: flex;
        column-gap: 18px;
        align-items: center;

        .preview-button {
          display: flex;
          column-gap: 8px;
          align-items: center;
          color: #175dff;
          cursor: pointer;

          &.plain {
            color: #2f3742;
          }
        }
      }
    }

    &-content {
      width: 100%;
      height: calc(100% - 24px);
      overflow: auto;
    }
  }
}
</style>

<style lang="scss">
.el-cascader-menu__wrap {
  height: 235px;
}
</style>
