<script setup lang="ts">
import { XMessage, debounce } from '@/hooks'
import CameraPreview from './CameraPreview.vue'
import ImageCropper from '@/components/ImageCropper/index.vue'

const props = withDefaults(
  defineProps<{
    submitAnswerPic?: string
    submitAnswerPicOriginal?: string
    isAndroid?: 0 | 1
    reportFlag: 0 | 1
    markAnswerPic?: string
    markAnswer?: string
    titleId: string
    uploadLoading?: boolean
    uploadFile: any
    beforeUpload: any
    isDeadline?: boolean
  }>(),
  { isDeadline: true },
)

const emit = defineEmits(['showPic', 'submit'])

const showCanvas = ref(false)
const showClearDialog = ref(false)
const activeTool = ref<'pen' | 'eraser'>('pen')
let canvasSessionId = 0

function resetCanvasToolbarState() {
  activeTool.value = 'pen'
}

function pen() {
  activeTool.value = 'pen'
  signaturePadRef.value?.handlePen?.()
}
function eraser() {
  activeTool.value = 'eraser'
  signaturePadRef.value?.handleEraser?.()
}

function requestClear() {
  showClearDialog.value = true
}

function confirmClear() {
  signaturePadRef.value?.handleClear?.()
  emit('submit', { id: props.titleId, clearCanvas: true })
}

function closeCanvas() {
  showClearDialog.value = false
  resetCanvasToolbarState()
  showCanvas.value = false
}

async function save() {
  const { blob, data } = await signaturePadRef.value.handleSaveAsync()

  if (!blob) {
    return XMessage.error('内容为空，无法保存')
  }

  if (typeof props.uploadFile !== 'function') {
    return XMessage.error('上传方法不存在，请重试')
  }

  try {
    const uploadSuccess = await props.uploadFile({ file: blob, submitAnswerPicOriginal: data })
    if (uploadSuccess !== false) {
      showCanvas.value = false
    }
  } catch (e) {
    console.error('uploadFile error', e)
    XMessage.error('上传失败，请重试')
  }
}

const showCropper = ref(false)
const cropperImg = ref<Blob | File | string>('')
const uploadQueue = ref<any[]>([])
// 串行化标记，避免并发上传
const isUploading = ref(false)

function onUploadCamera(options: any) {
  showCanvas.value = false
  uploadQueue.value.push(options)
  processQueue()
  return Promise.resolve()
}

function processQueue() {
  if (showCropper.value) return
  if (isUploading.value) return
  if (uploadQueue.value.length === 0) return

  const options = uploadQueue.value.shift()
  cropperImg.value = options.file
  showCropper.value = true
}

async function handleCropConfirm(blob: Blob) {
  if (typeof props.uploadFile !== 'function') {
    processQueue()
    return
  }
  // 在 await 期间 isCameraMode 可能因父组件状态变化而改变，先快照
  const replaceOnCameraUpload = !isCameraMode.value
  isUploading.value = true
  try {
    await props.uploadFile({
      file: blob,
      isAndroid: 0,
      fromCamera: true,
      replaceOnCameraUpload,
    })
  } catch (e) {
    console.error('uploadFile error', e)
  } finally {
    isUploading.value = false
    processQueue()
  }
}

function handleCropCancel() {
  processQueue()
}

async function handleStart() {
  if (props.reportFlag) {
    return emit('showPic', viewPic.value)
  }

  if (props.uploadLoading) {
    return XMessage.error('上传中，请稍后～')
  }

  showCanvas.value = true
}

const uploadRef = ref()

function handleCameraClick() {
  if (props.uploadLoading) return

  if (viewPic.value && !isCameraMode.value) {
    emit('submit', { id: props.titleId, clearCanvas: true })
    nextTick(() => {
      uploadRef.value?.$el.querySelector('input')?.click()
    })
  } else {
    uploadRef.value?.$el.querySelector('input')?.click()
  }
}

const signaturePadRef = ref()
const debounceFallback = debounce(() => signaturePadRef.value?.handleFallback(), 200)

const isCameraMode = computed(() => !!(props.submitAnswerPic && !props.submitAnswerPicOriginal))

const viewPic = computed(() => {
  const raw = props.submitAnswerPic || ''
  const arr = raw.split(',').filter(Boolean)
  const u = arr.length ? arr[arr.length - 1] : ''
  return String(u || '')
    .replace(/[`]/g, '')
    .replace(/http:\/\//gi, 'https://')
    .trim()
})

watch(
  () => props.titleId,
  async () => {
    canvasSessionId++
    resetCanvasToolbarState()
    showCanvas.value = false
  },
  { immediate: true },
)

watch(showCanvas, async (nv: boolean) => {
  if (!nv) {
    canvasSessionId++
    resetCanvasToolbarState()
    return
  }
  const currentSessionId = ++canvasSessionId
  resetCanvasToolbarState()
  await nextTick()
  if (!showCanvas.value || currentSessionId !== canvasSessionId) return
  pen()
  const bg = viewPic.value
  const data = props.isAndroid ? '' : props.submitAnswerPicOriginal || ''
  if (bg) {
    await signaturePadRef.value?.setBackgroundImage?.(bg)
  }
  if (!showCanvas.value || currentSessionId !== canvasSessionId) return
  if (data) {
    signaturePadRef.value?.handleLoad?.(data)
  }
})
</script>
<template>
  <!-- 拍照/手写切换 -->

  <div>
    <CameraPreview
      v-if="isCameraMode"
      :submit-answer-pic="submitAnswerPic"
      :report-flag="reportFlag"
      :upload-loading="uploadLoading"
      :upload-file="uploadFile"
      :before-upload="beforeUpload"
      @showPic="emit('showPic', $event)"
      @submit="emit('submit', $event)"
    />

    <div v-else class="hand-drawn">
      <div class="pic-box" @click="handleStart">
        <img v-if="viewPic" :src="viewPic" />
        <div v-show="uploadLoading" class="pic-mask">
          <Icon name="global_loading" loading size="96" />
        </div>
      </div>
      <div v-if="!props.reportFlag" class="upload-action">
        <div class="start-btn" @click="handleCameraClick">
          <mj-button :disabled="uploadLoading">相机/上传</mj-button>
        </div>
        <div class="upload-tip">
          <span class="upload-tip-dot"></span>
          最多上传3张图片
        </div>
      </div>
      <el-upload
        v-show="false"
        ref="uploadRef"
        accept="image/*"
        :before-upload="beforeUpload"
        :show-file-list="false"
        :disabled="uploadLoading"
        multiple
        :http-request="onUploadCamera"
      >
      </el-upload>
    </div>

    <div v-if="showCanvas && !isCameraMode" class="hand-drawn-mask">
      <div class="drawn-box">
        <SignaturePad ref="signaturePadRef" bg="#fff" @save="save"></SignaturePad>
      </div>
      <div class="tools">
        <mj-button
          :type="activeTool === 'pen' ? 'warn' : undefined"
          :class="{ 'tool-active': activeTool === 'pen' }"
          @click="pen"
        >
          画笔
        </mj-button>
        <mj-button
          :type="activeTool === 'eraser' ? 'warn' : undefined"
          :class="{ 'tool-active': activeTool === 'eraser' }"
          @click="eraser"
        >
          橡皮擦
        </mj-button>
        <mj-button @click="debounceFallback">上一步</mj-button>
        <mj-button @click="requestClear">清除</mj-button>
        <mj-button type="warn" @click="closeCanvas">取消</mj-button>
        <mj-button type="warn" :disabled="uploadLoading" @click="save">保存</mj-button>
      </div>
    </div>
    <slot name="markAnswer"></slot>
    <mj-dialog
      v-model="showClearDialog"
      title="提示"
      content="确认清空当前画布吗？清空后将恢复为白板。"
      ok-text="确认清除"
      cancel-text="取消"
      :mask-close="false"
      :z-index="10001"
      :ok="confirmClear"
    />
    <!-- <Other :markAnswerPic="markAnswerPic" :markAnswer="markAnswer" /> -->
    <ImageCropper
      v-model:visible="showCropper"
      :img="cropperImg"
      @confirm="handleCropConfirm"
      @cancel="handleCropCancel"
    />
  </div>
</template>
<style scoped lang="scss">
.hand-drawn {
  .pic-box {
    height: 22.67vh;
    border-radius: 1.67vh;
    border: 0.33vh solid #bed5fe;
    background: #f7faf9;
    position: relative;
    background-color: #fff;
    img {
      width: 100%;
      height: 100%;
      object-fit: contain;
    }
    .pic-mask {
      width: 100%;
      height: 100%;
      position: absolute;
      top: 0;
      left: 0;
      display: flex;
      align-items: center;
      justify-content: center;
      background-color: rgba(0, 0, 0, 0.2);
      border-radius: 1.67vh;
      z-index: 10;
    }
  }
  .start-btn {
    margin-top: 2vh;
    display: flex;
    justify-content: flex-end;
  }

  .upload-tip {
    margin-top: 10px;
    padding: 6px 14px;
    background: #fff7e6;
    border: 1px solid #ffd591;
    border-radius: 8px;
    color: #fa8c16;
    font-size: 14px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;

    .upload-tip-dot {
      width: 8px;
      height: 8px;
      border-radius: 50%;
      background: #fa8c16;
      flex-shrink: 0;
    }
  }
}
.hand-drawn-mask {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 10000;
  width: 100vw;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(4px);
  background: #f7faf9;
  .drawn-box {
    height: calc(100% - 100px);
  }
  .tools {
    display: flex;
    justify-content: flex-end;
    .mj-button {
      margin: 0 4px;
    }
    .tool-active {
      box-shadow: 0 0 0 2px rgba(250, 140, 22, 0.28);
      transform: translateY(-1px);
    }
  }
}
</style>
