<!-- eslint-disable vue/no-mutating-props -->
<script setup lang="ts">
import type { PaperQuestion } from '#/views/question-bank/paper-mgr'

import { Bottom, Delete, Edit, Plus, Top } from '@element-plus/icons-vue'

import { uploadImgToPath } from '#/api/global'
import { saveOrUpdateXkwQuestionTitle } from '#/api/service/question-bank/school-book'
import { usePaperStore } from '#/views/question-bank/paper-mgr'

defineOptions({ name: 'PaperQuestionItem' })

const props = defineProps<{
  index?: any
  // 预览模式
  preview?: boolean
  question: PaperQuestion
}>()

const {
  addQuestion,
  removeQuestion,
  moveUpQuestion,
  moveDownQuestion,
  isQuestionInPaper,
} = usePaperStore()

// 在试卷中
const isInPaper = computed(() => isQuestionInPaper(props.question))

function safeParseJson(val: any) {
  if (typeof val !== 'string') return undefined
  try {
    return JSON.parse(val)
  } catch {
    return undefined
  }
}

function toSimpleOptions(list: any[]) {
  return list.map((value: any, idx: number) => ({
    label: String.fromCharCode(65 + idx),
    value: value ?? '',
  }))
}

function normalizeOptionsFromOptionList(optionList: any[]) {
  if (!Array.isArray(optionList) || !optionList.length) return undefined

  let list = optionList
  while (Array.isArray(list) && list.length === 1 && Array.isArray(list[0])) {
    list = list[0]
  }

  if (
    list.every((i: any) => Array.isArray(i)) &&
    list.some((i: any) => Array.isArray(i) && i.length)
  ) {
    return (list as any[]).map((sub: any[]) => ({
      options: toSimpleOptions(sub),
    }))
  }

  if (list.every((i: any) => typeof i === 'string')) {
    return toSimpleOptions(list as any[])
  }

  if (list.every((i: any) => i && typeof i === 'object' && 'html' in i)) {
    return list.map((i: any) => ({
      label: i.index ?? i.label,
      value: i.html ?? i.value ?? '',
    }))
  }

  return list
}

const normalizedOptions = computed(() => {
  const q = props.question as any
  const rawOptions = q?.optionHtml ?? q?.optionList
  const parsed = safeParseJson(rawOptions)
  if (Array.isArray(parsed))
    return normalizeOptionsFromOptionList(parsed) ?? parsed
  if (Array.isArray(rawOptions))
    return normalizeOptionsFromOptionList(rawOptions)
  return normalizeOptionsFromOptionList(q?.optionList)
})

// 是否为完形填空
const isWanXing = computed(() => {
  const list = normalizedOptions.value as any[]
  if (!Array.isArray(list) || !list.length) return false
  for (const option of list) {
    if (option?.options && !option?.stem) return true
  }
  return false
})

// 是否为阅读理解
const isYueDu = computed(() => {
  const list = normalizedOptions.value as any[]
  if (!Array.isArray(list) || !list.length) return false
  for (const option of list) {
    if (option?.options && option?.stem) return true
  }
  return false
})

function willSimpleOptionsHorizontal(options: any) {
  if (!options?.length) return false
  let maxOptionLen = 0
  for (const option of options) {
    if (!option?.value) return true
    maxOptionLen = Math.max(maxOptionLen, String(option.value).length)
  }
  return maxOptionLen < 10
}

// 选项是否要水平显示
const optionsHorizontal = computed(() => {
  if (isWanXing.value || isYueDu.value) return false
  if (!normalizedOptions.value?.length) return false
  return willSimpleOptionsHorizontal(normalizedOptions.value)
})

// 显示答案那些
const answerVisible = ref(false)
const toggleAnswerVisible = () => {
  answerVisible.value = !answerVisible.value
}

// 显示header
const headerVisible = computed(() => {
  return !props.preview || answerVisible.value
})

// 题干前缀
const stemPrefix = computed(() => {
  return props.preview ? `（${props.question.score || 0}分）` : undefined
})

// 编辑题目
const editDialogVisible = ref(false)
const editSaving = ref(false)
const stemUploading = ref(false)
const stemEditorRef = ref<HTMLElement>()
const answerEditorRef = ref<HTMLElement>()
const explanationEditorRef = ref<HTMLElement>()
const stemUploadInputRef = ref<HTMLInputElement>()
const stemSelectionRange = ref<null | Range>(null)

const saveStemSelection = () => {
  const selection = window.getSelection()
  if (!selection || selection.rangeCount === 0) return
  const range = selection.getRangeAt(0)
  if (!stemEditorRef.value?.contains(range.commonAncestorContainer)) return
  stemSelectionRange.value = range.cloneRange()
}

const restoreStemSelection = () => {
  if (!stemSelectionRange.value) return
  const selection = window.getSelection()
  if (!selection) return
  selection.removeAllRanges()
  selection.addRange(stemSelectionRange.value)
}

const getUploadedFilePath = (res: any): string => {
  if (typeof res?.data?.filePath === 'string') return res.data.filePath
  if (typeof res?.filePath === 'string') return res.filePath
  if (typeof res?.data?.url === 'string') return res.data.url
  if (typeof res?.url === 'string') return res.url
  if (typeof res?.path === 'string') return res.path
  if (typeof res?.data === 'string') return res.data
  if (typeof res === 'string') return res
  return ''
}

const insertStemImage = (url: string) => {
  if (!stemEditorRef.value) return
  stemEditorRef.value.focus()
  restoreStemSelection()
  const selection = window.getSelection()
  if (!selection || selection.rangeCount === 0) {
    stemEditorRef.value.innerHTML += `<img src="${url}" alt="题目图片" />`
    return
  }
  const range = selection.getRangeAt(0)
  const img = document.createElement('img')
  img.src = url
  img.alt = '题目图片'
  range.deleteContents()
  range.insertNode(img)
  range.setStartAfter(img)
  range.setEndAfter(img)
  selection.removeAllRanges()
  selection.addRange(range)
  stemSelectionRange.value = range.cloneRange()
}

const triggerStemImageUpload = () => {
  stemUploadInputRef.value?.click()
}

const handleStemImageChange = async (event: Event) => {
  const input = event.target as HTMLInputElement
  const file = input.files?.[0]
  if (!file) return
  input.value = ''
  if (!file.type.startsWith('image/')) {
    ElMessage.warning('请上传图片文件')
    return
  }
  stemUploading.value = true
  try {
    const fd = new FormData()
    fd.append('file', file)
    const res: any = await uploadImgToPath(fd)
    const url = getUploadedFilePath(res)
    if (!url) {
      ElMessage.error('图片上传失败')
      return
    }
    insertStemImage(url)
    ElMessage.success('图片上传成功')
  } catch {
    ElMessage.error('图片上传失败，请重试')
  } finally {
    stemUploading.value = false
  }
}

const handleEdit = () => {
  const q = props.question as any
  editDialogVisible.value = true
  stemSelectionRange.value = null
  nextTick(() => {
    if (stemEditorRef.value) stemEditorRef.value.innerHTML = q.stem || ''
    if (answerEditorRef.value) answerEditorRef.value.innerHTML = q.answer || ''
    if (explanationEditorRef.value)
      explanationEditorRef.value.innerHTML = q.explanation || ''
  })
}

const handleEditSave = async () => {
  editSaving.value = true
  try {
    const q = props.question as any
    const stem = stemEditorRef.value?.innerHTML || ''
    const answer = answerEditorRef.value?.innerHTML || ''
    const explanation = explanationEditorRef.value?.innerHTML || ''
    await saveOrUpdateXkwQuestionTitle({
      id: q.id,
      stem,
      stemHtml: stem,
      answer,
      answerHtml: answer,
      explanation,
    })
    q.stem = stem
    q.answer = answer
    q.explanation = explanation
    editDialogVisible.value = false
    ElMessage.success('保存成功')
  } finally {
    editSaving.value = false
  }
}
</script>

<template>
  <div
    class="paper-question-item"
    :class="{ preview }"
    @click="toggleAnswerVisible"
  >
    <div class="middle">
      <span class="question-index">
        <span>{{ index }}</span>
      </span>
      <div v-if="question.stem" class="stem">
        <latex-text :text="question.stem" :prefix="stemPrefix" />
      </div>
      <template v-if="normalizedOptions?.length">
        <div v-if="isYueDu" class="options yuedu">
          <div
            class="option"
            v-for="(item, idx) in normalizedOptions"
            :key="idx"
          >
            <div class="index-stem">
              <span class="index">{{ idx + 1 }}</span>
              <latex-text :text="item.stem" />
            </div>
            <div
              class="sub-options"
              :class="{
                vertical: !willSimpleOptionsHorizontal(item.options),
              }"
            >
              <span
                class="sub-option"
                v-for="(subItem, subIdx) in item.options"
                :key="subIdx"
              >
                <span>{{ subItem.label }}.</span>
                <latex-text :text="subItem.value" />
              </span>
            </div>
          </div>
        </div>
        <div v-else-if="isWanXing" class="options">
          <div
            class="option"
            v-for="(item, idx) in normalizedOptions"
            :key="idx"
          >
            <span class="index">{{ idx + 1 }}</span>
            <div class="sub-options">
              <span
                class="sub-option"
                v-for="(subItem, subIdx) in item.options"
                :key="subIdx"
              >
                <span>{{ subItem.label }}.</span>
                <latex-text :text="subItem.value" />
              </span>
            </div>
          </div>
        </div>
        <div
          v-else
          class="options"
          :class="{
            horizontal: optionsHorizontal,
          }"
        >
          <div
            class="option"
            v-for="(item, idx) in normalizedOptions"
            :key="idx"
          >
            <span>{{ item.label }}.</span>
            <latex-text :text="item.value" />
          </div>
        </div>
      </template>
      <div
        v-if="preview && isInPaper"
        class="point-editor item-section"
        @click.stop
      >
        <span class="item-title">【评分标准】</span>
        <span class="item-content">
          <el-input
            v-model="question.point"
            type="textarea"
            placeholder="请输入评分标准"
            :autosize="{ minRows: 1, maxRows: 4 }"
          />
        </span>
      </div>
      <template v-if="answerVisible">
        <div v-if="question.answer" class="solve item-section">
          <span class="item-title">【答案】</span>
          <span class="item-content">
            <latex-text :text="question.answer" />
          </span>
        </div>
        <div v-if="question.explanation" class="analysis item-section">
          <span class="item-title">【解析】</span>
          <span class="item-content">
            <latex-text :text="question.explanation" />
          </span>
        </div>
      </template>
    </div>
    <div class="header" v-if="headerVisible">
      <div class="content">
        <div class="left">
          <!-- <template v-if="question.textBookName">
            <span class="book-chapter">{{ question.textBookName }}</span>
            <span class="divider">|</span>
          </template> -->
          <template v-if="question.titleChannelTypeName">
            <span class="type">{{ question.titleChannelTypeName }}</span>
            <span class="divider">|</span>
          </template>
          <template v-if="question.difficulty">
            <span class="difficulty"
              >{{ question.difficultyLevel }}（{{ question.difficulty }}）</span
            >
            <span class="divider">|</span>
          </template>
          <template v-if="question.year">
            <span class="difficulty">{{ question.year }}</span>
            <span class="divider">|</span>
          </template>
          <template v-if="question.linkPaper !== undefined">
            <span class="link-paper"
              >本人引用次数：{{ question.linkPaper }}</span
            >
          </template>
        </div>
        <div class="right">
          <template v-if="!preview">
            <el-button
              v-if="isInPaper"
              type="danger"
              :icon="Delete"
              @click.stop="removeQuestion(question)"
            >
              移出试卷
            </el-button>
            <el-button
              v-else
              type="primary"
              :icon="Plus"
              @click.stop="addQuestion(question)"
            >
              加入试卷
            </el-button>
          </template>
        </div>
      </div>
    </div>
    <div class="preview-toolbar" v-if="preview">
      <span class="score-editor" @click.stop>
        <span class="score-label">分值</span>
        <el-input-number
          v-model="question.score"
          :min="0"
          :max="999"
          :step="1"
          :precision="0"
          controls-position="right"
          placeholder="分"
          size="small"
        />
      </span>
      <el-button
        :icon="Edit"
        type="warning"
        @click.stop="handleEdit"
        title="编辑"
      />
      <el-button
        :icon="Top"
        type="primary"
        @click.stop="moveUpQuestion(question)"
        :disabled="question.isFirst"
        title="上移"
      />
      <el-button
        :icon="Bottom"
        type="primary"
        @click.stop="moveDownQuestion(question)"
        :disabled="question.isLast"
        title="下移"
      />
      <el-button
        type="danger"
        :icon="Delete"
        title="删除"
        @click.stop="removeQuestion(question)"
      />
    </div>

    <el-dialog
      v-model="editDialogVisible"
      title="编辑题目"
      width="900px"
      append-to-body
      :close-on-click-modal="false"
      @click.stop
    >
      <div class="edit-question-form">
        <div class="edit-field">
          <div class="edit-field-label-wrap">
            <div class="edit-field-label">题目</div>
            <el-button
              size="small"
              type="primary"
              :loading="stemUploading"
              @mousedown.prevent="saveStemSelection"
              @click="triggerStemImageUpload"
            >
              上传图片
            </el-button>
            <input
              ref="stemUploadInputRef"
              type="file"
              hidden
              accept="image/*"
              @change="handleStemImageChange"
            />
          </div>
          <div
            ref="stemEditorRef"
            class="edit-field-editor paper-question-item"
            contenteditable="true"
            @mouseup="saveStemSelection"
            @keyup="saveStemSelection"
            @blur="saveStemSelection"
          ></div>
        </div>
        <div class="edit-field">
          <div class="edit-field-label">答案</div>
          <div
            ref="answerEditorRef"
            class="edit-field-editor paper-question-item"
            contenteditable="true"
          ></div>
        </div>
        <div class="edit-field">
          <div class="edit-field-label">解析</div>
          <div
            ref="explanationEditorRef"
            class="edit-field-editor paper-question-item"
            contenteditable="true"
          ></div>
        </div>
      </div>
      <template #footer>
        <el-button @click="editDialogVisible = false">取消</el-button>
        <el-button type="primary" :loading="editSaving" @click="handleEditSave">
          保存
        </el-button>
      </template>
    </el-dialog>
  </div>
</template>

<style lang="scss">
.paper-question-item {
  font-family: 'Times New Roman', '宋体';
  line-height: 1.8;
  word-break: break-all;

  p {
    min-height: 24px;
    margin-top: 0;
    margin-bottom: 0;
    word-break: break-all;
  }

  img {
    vertical-align: middle;

    &.xkw-math-img {
      display: inline;
      max-height: 4em;
      margin: 0 2px;
      vertical-align: middle;
    }
  }

  table {
    margin-top: 6px;

    td,
    th {
      padding: 6px;
      border: 1px solid #333;
    }

    tr:nth-child(2n),
    tr {
      background-color: unset;
    }

    td {
      max-width: unset;
    }

    &[border] {
      table[border] {
        border-collapse: collapse;
        border-color: #efefef;
      }

      td p {
        padding: 0;
        line-height: 1;
      }
    }

    &[align='left'] {
      margin-right: auto;
      margin-left: 0;
    }

    &[align='center'] {
      margin-right: auto;
      margin-left: auto;
    }

    &[align='right'] {
      margin-right: 0;
      margin-left: auto;
    }

    td.slash-1 {
      background: #fff
        url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiPjxsaW5lIHgxPSIwIiB5MT0iMCIgeDI9IjEwMCUiIHkyPSIxMDAlIiBzdHJva2U9IiM2NjYiIHN0cm9rZS13aWR0aD0iMC41Ii8+PC9zdmc+')
        no-repeat 100% center;
    }
  }

  [wave] {
    white-space: pre-wrap;
    text-decoration-line: underline;
    text-decoration-style: wavy;
    text-underline-position: under;
  }

  [em] {
    white-space: pre-wrap;
    text-emphasis-style: circle;
    text-emphasis-position: under;
    text-emphasis-position: under right;
  }

  [u] {
    text-decoration: underline;
  }

  .bracket {
    text-decoration: none;
  }

  .style-bold {
    font-weight: bold;
  }

  .style-italic {
    font-style: italic;
  }

  .style-strike {
    text-decoration: line-through;
  }

  .style-underline {
    white-space: pre-wrap;
    text-decoration: underline;
  }

  .double-line {
    color: #333;
    border-bottom: 3px double #000;
  }

  .qml-ques {
    position: relative;
    display: block;
    margin-bottom: 20px;
    font-family: 'Times New Roman', '宋体';
    line-height: 1.8;
    word-break: break-all;

    table,
    p,
    & {
      font-size: 14px;
    }
  }

  .qml-stem {
    position: relative;
    margin-top: 10px;
    word-break: break-all;

    &:first-child {
      margin-top: 0;
    }

    img {
      vertical-align: middle;
    }
  }

  .qml-sq {
    position: relative;
    padding-left: 25px;

    .qml-stem {
      padding-left: 0;

      span.ques-no {
        display: inline-block;
        max-width: 40px;
        white-space: nowrap;
      }
    }
  }

  .qml-inline {
    display: inline;

    &.qml-og {
      display: inline-flex;
      width: calc(100% - 40px);

      span.qml-op {
        flex-grow: 1;
        flex-basis: 0;
      }
    }
  }

  .qml-bk {
    display: inline-block;
    line-height: 0.8;
    text-align: center;
    overflow-wrap: break-word;
    text-decoration-color: unset !important;
    background-color: #ecf9ff;
    border-bottom: 1px solid #000;
  }

  .qml-col1,
  .qml-col2,
  .qml-col3,
  .qml-col4 {
    width: 100%;

    td,
    th {
      border: none;
    }
  }

  table.qml-og {
    td,
    th {
      border: none;
    }
  }

  .qml-col1 td {
    width: 100%;
  }

  .qml-col2 td {
    width: 50%;
  }

  .qml-col3 td {
    width: 33.3%;
  }

  .qml-col4 td {
    width: 25%;
  }

  .qml-answer {
    margin-bottom: 5px;
    margin-left: 10px;
    word-break: break-all;

    img {
      vertical-align: middle;
    }
  }

  .qml-an {
    margin-right: 10px;
  }

  /* stylelint-disable-next-line block-no-empty */
  .qml-an-sq {
  }

  .qml-block-ans {
    display: block;
  }

  .qml-inline-ans {
    display: inline-block;
    margin-right: 15px;
  }

  .qml-explanation {
    .qml-seg p,
    .qml-exps-sq {
      margin-left: 10px;
    }

    img {
      vertical-align: middle;
    }
  }

  .bk-mp {
    margin-left: 10px;
  }

  span.title-number + table.qml-og {
    position: relative;
    top: -3px;
  }
}
</style>

<style lang="scss" scoped>
$fz: 16px;
$padding: 16px;

@mixin index {
  min-width: 24px;
  height: 24px;
  padding: 0 4px;
  line-height: 24px;
  color: #246cff;
  text-align: center;
  background-color: #f4f9ff;
  border-radius: 4px;
}

.paper-question-item {
  $header_h: 48px;

  position: relative;
  font-size: $fz;
  cursor: pointer;
  background-color: #fff;
  border: 1px solid transparent;
  border-radius: 4px;

  .preview-toolbar {
    position: absolute;
    top: -50px;
    right: -1px;
    display: none;
    gap: 10px;
    align-items: center;
    height: 50px;
    padding: 0 10px;
    background-color: #fff;
    border: 1px solid #246cff;
    border-top-left-radius: 4px;
    border-top-right-radius: 4px;

    .score-editor {
      display: flex;
      gap: 4px;
      align-items: center;
      margin-right: 6px;

      .score-label {
        font-size: 13px;
        color: #606266;
        white-space: nowrap;
      }

      .el-input-number {
        width: 90px;

        :deep(.el-input__inner) {
          font-weight: 600;
          color: #246cff;
        }
      }
    }
  }

  &.preview {
    border-radius: 4px;
    border-top-right-radius: 0;

    .header {
      visibility: hidden;
    }

    .preview-toolbar {
      display: flex;
      border-color: transparent;

      .el-button {
        opacity: 0;
        transition: opacity 0.2s;
      }
    }

    &:hover {
      border-color: #246cff;

      .header {
        visibility: visible;
      }

      .preview-toolbar {
        border-color: #246cff;

        .el-button {
          opacity: 1;
        }
      }
    }
  }

  .content {
    display: flex;
    flex: 1;
    align-items: center;
    justify-content: space-between;
    height: $header_h;
    padding: 0 20px;
  }

  .header {
    display: flex;
    align-items: center;
    height: $header_h;
    border-top: 1px dashed #dcdcdc;

    &.operations {
      height: $header_h + 5px;
    }

    .chaper-names {
      flex: 1;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    .divider {
      padding: 0 16px;
      color: #ececec;
    }

    .chaper-names,
    .year,
    .type,
    .difficulty,
    .book-chapter,
    .link-paper {
      color: #666;
    }

    .knowledge {
      color: #999;
    }
  }

  .middle {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: $padding;
    padding: 10px 20px;

    .question-index {
      position: absolute;
      top: 10px;
      left: 20px;
      display: flex;
      align-items: center;
      justify-content: center;
      height: 24px;
      font-size: $fz;
      color: #246cff;

      &::after {
        content: '.';
      }
    }

    .stem {
      padding-left: 2em;
    }

    :deep(.latex-text) {
      font-size: $fz;
    }

    .options {
      display: flex;
      flex-direction: column;
      gap: 16px;
      padding: 0 16px;

      &.horizontal {
        flex-direction: row;

        .option {
          flex: 1;
        }
      }

      &.yuedu {
        .option {
          flex-direction: column;
        }

        .index-stem {
          display: flex;
          gap: 10px;
        }
      }

      .option {
        display: flex;
        gap: 10px;

        .index {
          @include index;
        }

        .sub-options {
          display: flex;
          gap: 20px;

          &.vertical {
            flex-direction: column;

            .sub-option {
              width: 100%;
            }
          }

          .sub-option {
            display: flex;
            gap: 10px;
            width: 180px;
            padding-left: 2em;
          }
        }
      }
    }

    .item-section {
      display: flex;
      align-items: flex-start;
      padding-top: 16px;
      border-top: 1px dashed #dcdcdc;

      .item-title {
        min-width: 110px;
        color: #246cff;
        white-space: nowrap;
      }

      .item-content {
        flex: 1;
      }
    }

    .data {
      padding: 16px;
    }
  }

  .point-editor {
    :deep(.el-textarea__inner) {
      border-radius: 8px;
    }
  }

  .answer-list {
    display: flex;
    flex-wrap: wrap;
    gap: $padding;
    align-items: center;

    .answer-list-item {
      display: flex;
      gap: 8px;
      align-items: center;

      .sub-answers {
        display: flex;
        gap: 8px;
        align-items: center;
      }

      .answer-index {
        @include index;
      }
    }
  }

  .left {
    display: flex;
    gap: 16px;
    align-items: center;
  }

  .right {
    display: flex;
    gap: $padding;
    align-items: center;
  }

  .el-button + .el-button {
    margin-left: 0;
  }
}

.edit-question-form {
  display: flex;
  flex-direction: column;
  gap: 20px;
  max-height: 65vh;
  overflow-y: auto;

  .edit-field {
    display: flex;
    flex-direction: column;
    gap: 8px;

    .edit-field-label-wrap {
      display: flex;
      gap: 8px;
      align-items: center;
      justify-content: space-between;
    }

    .edit-field-label {
      font-size: 15px;
      font-weight: 600;
      color: #303133;
    }

    .edit-field-editor {
      min-height: 60px;
      padding: 12px 16px;
      line-height: 1.8;
      cursor: text;
      outline: none;
      border: 1px solid #dcdfe6;
      border-radius: 4px;
      transition: border-color 0.2s;

      &:focus {
        border-color: #409eff;
      }

      &:empty::before {
        color: #c0c4cc;
        content: '请输入内容';
      }
    }
  }
}
</style>
