<route lang="json5" type="page">
{
  layout: 'default',
  style: {
    navigationBarTitleText: '',
    navigationStyle: 'custom',
    disableScroll: true,
  },
}
</route>

<!-- 检验方案编辑 -->
<template>
  <div>
    <SpreadDesignerGeneric
      ref="designerRef"
      :businessConfig="businessConfig"
      :templateData="templateData"
      :templateBlob="templateBlob"
      @save="handleSave"
      @cancel="handleCancel"
      @customAction="handleCustomAction"
    >
      <template #navBar>
        <ReportNavBar>
          <template #right>
            <view class="nav-btn-group">
              <view
                class="nav-btn"
                @click="designerRef?.handleNavButtonClick({ action: 'cancel' })"
              >
                取消
              </view>
              <view
                class="nav-btn primary"
                @click="designerRef?.handleNavButtonClick({ action: 'save' })"
              >
                保存
              </view>
              <view
                class="nav-btn upload-btn"
                @click="designerRef?.handleNavButtonClick({ action: 'upload' })"
              >
                方案上传
              </view>
              <view
                class="nav-btn submit-btn"
                @click="designerRef?.handleNavButtonClick({ action: 'submit' })"
              >
                提交审核
              </view>
            </view>
          </template>
        </ReportNavBar>
      </template>
    </SpreadDesignerGeneric>

    <!-- 审核人选择弹窗 -->
    <view v-if="auditorVisible" class="auditor-overlay" @click="auditorVisible = false">
      <view class="auditor-dialog" @click.stop>
        <view class="auditor-title">选择审核人</view>
        <scroll-view scroll-y class="auditor-list">
          <view
            v-for="item in auditorList"
            :key="item.id"
            :class="['auditor-item', { active: selectedAuditorId === item.id }]"
            @click="selectedAuditorId = item.id"
          >
            <text class="auditor-name">{{ item.nickname || item.userName }}</text>
            <view v-if="selectedAuditorId === item.id" class="auditor-check">&#10003;</view>
          </view>
          <view v-if="auditorLoading" class="auditor-loading">加载中...</view>
          <view v-if="!auditorLoading && auditorList.length === 0" class="auditor-empty">
            暂无可选审核人
          </view>
        </scroll-view>
        <view class="auditor-footer">
          <view class="auditor-btn cancel" @click="auditorVisible = false">取消</view>
          <view class="auditor-btn confirm" @click="confirmSubmit">确定</view>
        </view>
      </view>
    </view>
  </div>
</template>

<script setup lang="ts">
import { ref, onUnmounted } from 'vue'
import SpreadDesignerGeneric from '@/components/SpreadDesigner/spreadDesignerGeneric.vue'
import ReportNavBar from '@/components/NavBar/ReportNavBar.vue'
import { onLoad } from '@dcloudio/uni-app'
import { getEnvBaseUrl } from '@/utils/index'
import { getStandardTemplate, uploadFile } from '@/api/index'
import { getAuditList } from '@/api/system/user'
import { getDynamicTbVal, saveDynamicTbVal } from '@/api/task'
import { useConfigStore } from '@/store/config'
import { useUserStore } from '@/store'
import { EquipmentType, PressureReportType } from '@/utils/dictMap'
import { requestFunc, TaskOrderFuncName } from '@/api/ApiRouter/taskOrder'
import dayjs from 'dayjs'

const businessConfig = ref({
  businessType: 'JYFA',
  title: '检验方案编辑',
  disableNavigate: true,

  ui: {
    title: '检验方案',
    saveButtonText: '保存',
    cancelButtonText: '取消',
    showAdditionalToolbar: true,
    customButtons: [
      {
        text: '方案上传',
        class: 'upload-btn',
        action: 'upload',
      },
      {
        text: '提交审核',
        class: 'submit-btn',
        action: 'submit',
      },
    ],
  },
})
const skipEntryKeys = ref<string[]>([])
const templateBlob = ref<string>('')
const templateData = ref<any>({})
const equipType = useConfigStore().getEquipType()
const userStore = useUserStore()
const userInfo = userStore.userInfo

let templateId: string = ''
let refId: string = ''
let manualUrl: string = ''

onLoad((options: any) => {
  templateId = options.templateId
  refId = options.refId
  manualUrl = options.manualUrl || ''
  // 注册RN文件选择回调
  if (isRNEnv()) {
    registerRNFileCallback()
  }
  init()
})

onUnmounted(() => {
  if ((window as any).onFileSelectedFromRN) {
    delete (window as any).onFileSelectedFromRN
  }
})

const instId = ref<string>('')
const init = async () => {
  uni.hideLoading()
  skipEntryKeys.value = []
  if (!templateId || !refId) {
    return
  }

  const res = await getStandardTemplate({ id: templateId })
  const resData = (res as any).data
  const dataMap: any = {}

  const dynamicTbValResp = await getDynamicTbVal({ refId, templateId })
  const dynamicTb: any = dynamicTbValResp.data
  instId.value = dynamicTb?.dynamicTbInsRespVO?.id || ''
  for (let i = 0; i < dynamicTb.dynamicTbValRespVOList.length; i++) {
    const item = dynamicTb.dynamicTbValRespVOList[i]
    const val = item.valValue
    if (typeof val === 'string' && /\.(jpg|png)$/i.test(val)) {
      skipEntryKeys.value.push(item.colCode)
      continue
    }
    dataMap[item.colCode] = val
  }

  templateData.value = {
    schema: resData.bindingPathSchema ? JSON.parse(resData.bindingPathSchema) : {},
    data: {
      ...dataMap,
      templateId,
      templateUrl: resData.fileUrl,
    },
    pathNameMapping: JSON.parse(resData.bindingPathNameJson),
    template: templateId,
    templateUrl: resData.fileUrl,
  }

  const excelBlob = await fetchExcel(refId, templateId)
  const fileBase64 = await blobToBase64(excelBlob)
  templateBlob.value = fileBase64
}

const fetchExcel = async (refId: string | number, templateId: string | number): Promise<Blob> => {
  const apiPath = '/pressure2/excel/excel'
  let requestUrl = apiPath
  if (JSON.parse(import.meta.env.VITE_APP_PROXY) && import.meta.env.MODE === 'development') {
    requestUrl = import.meta.env.VITE_APP_PROXY_PREFIX + apiPath
  } else {
    requestUrl = getEnvBaseUrl() + apiPath
  }

  const token = uni.getStorageSync('APP_ACCESS_TOKEN')
  const headers: Record<string, string> = { 'Content-Type': 'application/json' }
  if (token) {
    headers.Authorization = `Bearer ${token}`
  }

  const response = await fetch(requestUrl, {
    method: 'POST',
    headers,
    body: JSON.stringify({ refId, templateId }),
  })

  if (!response.ok) {
    throw new Error(`获取Excel失败, status ${response.status}`)
  }

  return await response.blob()
}

const blobToBase64 = (blob: Blob): Promise<string> => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.onload = () => {
      const base64 = (reader.result as string).split(',')[1]
      resolve(base64)
    }
    reader.onerror = reject
    reader.readAsDataURL(blob)
  })
}

const handleSave = async (data: any) => {
  const reqData = {}
  for (const key in data.dataJSON) {
    if (skipEntryKeys.value.includes(key)) {
      continue
    }
    const element = data.dataJSON[key];
    reqData[key] = element
  }
  const result = await saveDynamicTbVal({ params: reqData, instId: instId.value })
  if (result?.code === 0 && result?.data) {
    uni.showToast({ title: '保存报表成功', icon: 'success' })
  } else {
    const msg = result?.msg || '保存报表失败'
    uni.showToast({ title: msg, icon: 'error' })
  }
}

const handleCancel = () => {
  uni.navigateBack()
}

const designerRef = ref<any>(null)

// 检测RN WebView环境
const isRNEnv = () => {
  return !!(window as any).ReactNativeWebView
}

// 注册RN文件选择回调
const registerRNFileCallback = () => {
  ;(window as any).onFileSelectedFromRN = handleFileFromRN
}

// 暂存待上传的文件
const pendingFile = ref<{ base64: string; name: string; type: string; size: number } | null>(null)

// 审核人选择相关
const auditorVisible = ref(false)
const auditorList = ref<any[]>([])
const auditorLoading = ref(false)
const selectedAuditorId = ref<string>('')

// 处理RN回传的文件数据
const handleFileFromRN = async (fileData: any) => {
  if (fileData.canceled) return
  if (fileData.error) {
    uni.showToast({ title: fileData.error, icon: 'none' })
    return
  }
  storeFile(fileData.base64, fileData.name, fileData.type, fileData.size)
}

// 暂存文件到内存，已存在则弹窗确认覆盖
const storeFile = (base64: string, name: string, mimeType: string, size: number) => {
  if (pendingFile.value) {
    uni.showModal({
      title: '提示',
      content: `当前已选择文件「${pendingFile.value.name}」，是否覆盖为「${name}」？`,
      confirmText: '覆盖',
      cancelText: '取消',
      success: (res) => {
        if (res.confirm) {
          pendingFile.value = { base64, name, type: mimeType, size }
          uni.showToast({ title: '文件已更新', icon: 'success' })
        }
      },
    })
  } else {
    pendingFile.value = { base64, name, type: mimeType, size }
    uni.showToast({ title: '文件已选择', icon: 'success' })
  }
}

// 执行文件上传，返回文件URL
const doUpload = async (): Promise<string | null> => {
  if (!pendingFile.value) return null
  const { base64, name, type: mimeType } = pendingFile.value
  try {
    uni.showLoading({ title: '正在上传...' })
    const byteString = atob(base64)
    const ab = new ArrayBuffer(byteString.length)
    const ia = new Uint8Array(ab)
    for (let i = 0; i < byteString.length; i++) {
      ia[i] = byteString.charCodeAt(i)
    }
    const blob = new Blob([ab], { type: mimeType || 'application/octet-stream' })
    const file = new File([blob], name, { type: mimeType || 'application/octet-stream' })

    const formData = new FormData()
    formData.append('file', file)
    const result = await uploadFile(formData)

    uni.hideLoading()

    if (result?.code === 0 && result.data) {
      return result.data as string
    } else {
      uni.showToast({ title: result?.msg || '上传失败', icon: 'none' })
      return null
    }
  } catch (error) {
    uni.hideLoading()
    console.error('上传文件异常:', error)
    uni.showToast({ title: '上传失败', icon: 'none' })
    return null
  }
}

// 选择文件（RN环境）
const pickFileRN = () => {
  ;(window as any).ReactNativeWebView.postMessage(
    JSON.stringify({
      type: 'selectFile',
      allowedTypes: [
        'application/pdf',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
      ],
      multiple: false,
    }),
  )
}

// 选择文件（非RN环境）
const pickFileH5 = () => {
  const input = document.createElement('input')
  input.type = 'file'
  input.accept = '.pdf,.docx'
  input.style.display = 'none'
  input.onchange = async (e: Event) => {
    const file = (e.target as HTMLInputElement).files?.[0]
    if (!file) return
    const reader = new FileReader()
    reader.onload = async () => {
      const base64 = (reader.result as string).split(',')[1]
      storeFile(base64, file.name, file.type, file.size)
    }
    reader.readAsDataURL(file)
    document.body.removeChild(input)
  }
  document.body.appendChild(input)
  input.click()
}

// 打开审核人选择弹窗
const openAuditorDialog = async () => {
  auditorVisible.value = true
  auditorLoading.value = true
  auditorList.value = []
  selectedAuditorId.value = ''
  try {
    const roleCode = equipType === EquipmentType.BOILER ? 'Boiler Director' : 'Pipeline Director'
    const res = await getAuditList({ roleCode })
    auditorList.value = (res as any)?.data?.list || []
  } catch (e) {
    console.error('获取审核人列表失败:', e)
    uni.showToast({ title: '获取审核人列表失败', icon: 'none' })
  } finally {
    auditorLoading.value = false
  }
}

// 确认提交：上传文件后调用提交接口
const confirmSubmit = async () => {
  if (!selectedAuditorId.value) {
    uni.showToast({ title: '请选择审核人', icon: 'none' })
    return
  }
  auditorVisible.value = false
  const fileUrl = await doUpload()
  if (!fileUrl) {
    uni.showToast({ title: '请先上传检验方案文件', icon: 'none' })
    return
  }
  try {
    const dataSource = designerRef.value?.getSpreadDataSource()
    if (dataSource) {
      const saveResult = await saveDynamicTbVal({ params: dataSource, instId: instId.value })
      if (saveResult?.code !== 0) {
        throw new Error(saveResult?.msg || '保存报表失败，请重试')
      }
    }
    const reqData = {
      id: refId,
      reportType: PressureReportType.INSPECTIONPLAN,
      prepareJson: JSON.stringify({
        prepareName: uni.getStorageSync('userInfo')?.name || '',
        prepareDate: dayjs().format('YYYY年MM月DD日'),
      }),
      auditUserIds: [selectedAuditorId.value],
      approveUserIds: [],
      manualUrl: fileUrl,
    }
    uni.showLoading({ title: '提交审核中...', mask: true })
    // 调用提交审核接口
    const res = await requestFunc(TaskOrderFuncName.SubmitOpinionNoticeApproval, equipType, reqData)
    if (res?.code === 0) {
      uni.showToast({ title: '提交审核成功', icon: 'success' })
    } else {
      throw new Error(res?.msg || '提交审核失败，请重试')
    }
  } catch (error) {
    console.error('提交审核异常:', error)
    uni.showToast({ title: error.message || '提交审核失败', icon: 'none' })
  } finally {
    uni.navigateBack()
    uni.hideLoading()
  }
}

const handleCustomAction = ({ action, data }) => {
  switch (action) {
    case 'upload':
      isRNEnv() ? pickFileRN() : pickFileH5()
      break
    case 'submit':
      if (!pendingFile.value) {
        uni.showToast({ title: '请先上传检验方案文件', icon: 'none' })
        break
      }
      uni.showModal({
        title: '提交审核',
        content: `检验方案文件目前为「${pendingFile.value.name}」，确认提交审核？`,
        confirmText: '确定',
        cancelText: '取消',
        success: async (res) => {
          if (!res.confirm) return
          await openAuditorDialog()
        },
      })
      break
    default:
      break
  }
}
</script>

<style scoped>
.nav-btn-group {
  display: flex;
  gap: 8px;
  align-items: center;
}

.nav-btn {
  padding: 0 12px;
  font-size: 12px;
  line-height: 28px;
  color: #495057;
  white-space: nowrap;
  background-color: #fff;
  border: 1px solid #dee2e6;
  border-radius: 4px;
}

.nav-btn.primary {
  color: white;
  background-color: #007bff;
  border-color: #007bff;
}

.nav-btn.upload-btn {
  color: white;
  background-color: #e6a23c;
  border-color: #e6a23c;
}

.nav-btn.submit-btn {
  color: white;
  background-color: #67c23a;
  border-color: #67c23a;
}

/* 审核人弹窗 */
.auditor-overlay {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 10001;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: rgba(0, 0, 0, 0.5);
}

.auditor-dialog {
  width: 80%;
  max-height: 70vh;
  overflow: hidden;
  background: #fff;
  border-radius: 12px;
}

.auditor-title {
  padding: 16px;
  font-size: 16px;
  font-weight: 600;
  color: #333;
  text-align: center;
  border-bottom: 1px solid #eee;
}

.auditor-list {
  max-height: 50vh;
  padding: 8px 0;
}

.auditor-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  cursor: pointer;
}

.auditor-item.active {
  background-color: #ecf5ff;
}

.auditor-name {
  font-size: 14px;
  color: #333;
}

.auditor-check {
  font-size: 16px;
  color: #007bff;
}

.auditor-loading,
.auditor-empty {
  padding: 20px;
  font-size: 14px;
  color: #999;
  text-align: center;
}

.auditor-footer {
  display: flex;
  border-top: 1px solid #eee;
}

.auditor-btn {
  flex: 1;
  padding: 14px 0;
  font-size: 15px;
  text-align: center;
  cursor: pointer;
}

.auditor-btn.cancel {
  color: #666;
  border-right: 1px solid #eee;
}

.auditor-btn.confirm {
  font-weight: 600;
  color: #007bff;
}
</style>
