<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useEventListener } from '@vueuse/core'
import axios from 'axios'
import { IFrame } from '@/components/IFrame'

import { isInIframe } from '@/utils/iframe'

import { useMessage } from '@/hooks/web/useMessage'
import { uploadOneFile } from '@/api/base/upload'
import { initFilePreviewUrl } from '@/utils/file/preview'

import { getConfigKey } from '@/api/infra/config'
import type { FileDO } from '@/types/axios'
import { useGlobSetting } from '@/hooks/setting'
import { getAccessToken } from '@/utils/auth'
import {
  base64ToBlob,
  encodeBase64,
  extractMethodName,
  isHttps,
} from '@/views/infra/bookmark/logic'

defineOptions({ name: 'BookmarkReplace' })
const isEmbed = isInIframe()
const globSetting = useGlobSetting()

const previewUrl = ref<string | undefined>(undefined)
const iframeRef = ref<InstanceType<typeof IFrame> | null>(null)

const { createMessage } = useMessage()

const wopi_client = ref(undefined)
const wopi_server = ref(undefined)

const editStatus = ref<{ Modified: boolean }>({ Modified: false })
const currentEditFile = ref<FileDO | undefined>(undefined)

const isPostMessageRead = ref<boolean>(false)
onMounted(async () => {
  if (isHttps()) {
    wopi_client.value = await getConfigKey('wopi_client_addr')
    wopi_server.value = await getConfigKey('wopi_server_ip_addr')
  }
  else {
    wopi_client.value = await getConfigKey('wopi_client_addr_http')
    wopi_server.value = await getConfigKey('wopi_server_ip_addr_http')
  }

  /**
   * 向第三方调用发送准备好消息
   */
  sendMessageToCaller({
    ActionId: 'WOPIClientReady',
    Payload: null,
    Timestamp: Date.now(),
  }, true)

  /**
   * 接收消息
   */
  useEventListener(window, 'message', async (e: MessageEvent) => {
    logEvent(e)

    let data: any = null

    if (typeof e.data === 'string')
      data = JSON.parse(e.data)

    if (typeof e.data === 'object')
      data = e.data

    if (!data)
      return

    let ActionId = ''
    let MessageId = ''
    if (data)
      ActionId = data.ActionId

    if (data.MessageId)
      MessageId = data.MessageId

    // 第三方调用发送的Event
    switch (ActionId) {
      case 'OpenFile':
        await handleFileBinary(data)
        break
      case 'QueryAllWithJSON':
        proxyQueryBookmark()
        break
      case 'ReplaceWithJSON':
        proxyReplaceWithJSON(data)
        break
      case 'SaveFile':
        handleSaveFile()
        break
      case 'AddBookmark':
        handleAddBookmark(data)
        break
      case 'LocateBookmark':
        handleLocateBookmark(data)
        break
      case 'DeleteBookmark':
        handleDeleteBookmark(data)
        break
      case 'InsertRowWithJSON':
        handleInsertRowWithJSON(data)
        break
      case 'DeleteRowWithJSON':
        handleDeleteRowWithJSON(data)
        break
      case 'ModifyStatus':
        handleSendModifyStatus()
        break
      case 'ReplaceTextAndInsertTableRow':
        handleReplaceTextAndInsertTableRow(data)
        break
      case 'ReplaceTextAndInsertTableRowWithContentControl':
        handleReplaceTextAndInsertTableRowWithContentControl(data)
        break
      case 'NextEditableZone':
        handleNextEditableZone()
        break
    }
    // WOPI Client发送消息
    switch (MessageId) {
      // 更新文档是否被编辑
      case 'Doc_ModifiedStatus':
        handleUpdateModifiedStatus(data)
        break
      // 用户执行了保存操作
      case 'UI_Save':
        handleUserSaveOp()
        break
      case 'CallPythonScript-Result':
        handlePythonScriptCallBack(data)
        break
      case 'Action_Save_Resp':
        handleUserSaveOp()
        break
      case 'App_LoadingStatus':
        handleDocumentReady(data)
        break
    }
  })
})

/**
 * 更新是否编辑状态
 * 保存后会更新是否编辑为否，正常编辑后会更新是否编辑为是
 * 用于区分是否要向第三方发送保存文件的回调
 * @param data
 */
function handleUpdateModifiedStatus(data: any) {
  editStatus.value.Modified = data.Values.Modified
}

let pollingInterval: NodeJS.Timeout | null = null
let lastCallTime: number | null = null

async function handleUserSaveOp() {
  // 情况 1: 如果当前状态未修改，直接发送消息
  if (!editStatus.value.Modified) {
    // 下载文件并发送
    downloadAndSendCurrentEditFile()
    return
  }

  // 情况 2: 已处于修改状态，开始/重置等待流程
  // 更新最后一次调用时间戳
  lastCallTime = Date.now()

  // 清除已有定时器（实现调用重置）
  if (pollingInterval) {
    clearInterval(pollingInterval)
    pollingInterval = null
  }

  // 启动新的轮询检测
  pollingInterval = setInterval(() => {
    // 子情况 2a: 状态已恢复未修改
    if (!editStatus.value.Modified) {
      downloadAndSendCurrentEditFile()
      cleanup()
      return
    }

    // 子情况 2b: 检测超时（10秒逻辑）
    const currentTime = Date.now()
    if (lastCallTime && currentTime - lastCallTime >= 10000) {
      createMessage.error('保存操作执行超时！')
      cleanup()
    }
  }, 500) // 每 500ms 检测一次
}

// 清理函数
function cleanup() {
  if (pollingInterval) {
    clearInterval(pollingInterval)
    pollingInterval = null
  }
  lastCallTime = null
}

/**
 * 下载当前编辑的文件，发送给调用者
 */
async function downloadAndSendCurrentEditFile() {
  if (!currentEditFile.value)
    return
  try {
    // 1. 发送请求（关键配置 responseType: 'arraybuffer'）
    const response = await axios.get(`${globSetting.apiUrl}/infra/file/download/${currentEditFile.value.id}`, {
      responseType: 'arraybuffer',
      headers: {
        Authorization: `Bearer ${getAccessToken()}`,
      },
    })

    // 2. 验证响应状态
    if (response.status !== 200)
      createMessage.error(`请求失败，状态码：${response.status}`)

    // 3. 获取 ArrayBuffer 数据
    const arrayBuffer: ArrayBuffer = response.data

    sendMessageToCaller({
      ActionId: 'SaveFile',
      Payload: {
        name: currentEditFile.value.name,
        buffer: encodeBase64(arrayBuffer),
        type: currentEditFile.value.type,
        size: currentEditFile.value.size,
      },
      Timestamp: Date.now(),
    }, true)
  }
  catch (error) {
    console.error('文件下载失败:', error)
    createMessage.error('文件下载失败，请查看控制台日志')
    throw error
  }
}

/**
 * 第三方上传文件
 * @param data
 */
async function handleFileBinary(data: any) {
  isPostMessageRead.value = false
  const { name, type, buffer } = data.Payload
  const blob = base64ToBlob(buffer, type)
  const uploadResult = await uploadOneFile({
    filename: name,
    file: new File([blob], name, { type }),
  })
  currentEditFile.value = uploadResult.data.data as FileDO
  previewUrl.value = await initFilePreviewUrl(currentEditFile.value.id, wopi_client.value, wopi_server.value)
}

/**
 * 第三方调用，查询所有书签
 */
function proxyQueryBookmark() {
  sendMessageToWopiClient({
    MessageId: 'CallPythonScript',
    SendTime: Date.now(),
    ScriptFile: 'BookmarkOP.py',
    Function: 'QueryAllWithJSON',
  })
}

/**
 * 第三方调用，书签替换
 * @param data
 */
function proxyReplaceWithJSON(data: any) {
  sendMessageToWopiClient({
    MessageId: 'CallPythonScript',
    SendTime: Date.now(),
    ScriptFile: 'BookmarkOP.py',
    Function: 'ReplaceWithJSON',
    Values: {
      params: {
        type: 'string',
        value: JSON.stringify(data.Payload),
      },
    },
  })
}

/**
 * 发送消息给WopiClient
 * 用于执行书签相关操作
 */
function sendMessageToWopiClient(data: any) {
  try {
    if (!isPostMessageRead.value) {
      iframeRef.value?.sendMessageToIframe(JSON.stringify({
        MessageId: 'Host_PostmessageReady',
        SendTime: Date.now(),
      }))
      isPostMessageRead.value = true
    }

    iframeRef.value?.sendMessageToIframe(JSON.stringify(data))
    console.log('已向WOPI_Client发送消息')
    console.log(JSON.stringify(data))
  }
  catch (e) {
    console.error(e)
    createMessage.error(`消息发送失败，查看控制台日志！`)
  }
}

/**
 * 发送消息给第三方调用者
 * 用于转发部分WopiClient回调
 *
 * ignore：忽略类型
 */
function sendMessageToCaller(data: any, ignore: boolean = false) {
  console.log('已向第三方调用者发送消息')
  console.log(JSON.stringify(data))
  try {
    const targetWindow = window.parent

    if (ignore) {
      targetWindow.postMessage(data, '*')
      return
    }

    if (typeof data === 'string')
      targetWindow.postMessage(data, '*')

    if (typeof data === 'object')
      targetWindow.postMessage(JSON.stringify(data), '*')
  }
  catch (e) {
    console.error(e)
    createMessage.error(`消息发送失败，查看控制台日志！`)
  }
}

/**
 * Python脚本执行回调，转发消息个第三方调用者
 * @param data
 */
function handlePythonScriptCallBack(data: any) {
  try {
    const Values = data?.Values
    const success = Values?.success as boolean
    const commandName = Values?.commandName
    if (success && Values) {
      if (Values?.result?.value) {
        const jsonData = JSON.parse(Values.result.value)
        sendMessageToCaller({
          ActionId: extractMethodName(commandName),
          Success: success,
          Payload: jsonData,
          Message: null,
          Timestamp: Date.now(),
        })
      }
    }
    else {
      console.error(data)
      createMessage.error(`执行Python脚本失败: ${data.Values.result.value}`)

      sendMessageToCaller({
        ActionId: extractMethodName(commandName),
        Success: success,
        Payload: null,
        Message: data.Values.result.value,
        Timestamp: Date.now(),
      })
    }
  }
  catch (e) {
    console.error(e)
    createMessage.error(`消息发送失败，查看控制台日志！`)
  }
}

/**
 * 处理第三方调用者请求保存文件
 */
function handleSaveFile() {
  sendMessageToWopiClient({
    MessageId: 'Action_Save',
    Values: {
      DontTerminateEdit: true,
      DontSaveIfUnmodified: false,
      Notify: true,
      ExtendedData: '',
    },
  })
}

/**
 * 第三方调用者在选中文字或光标位置处插入书签
 * @param data
 */
function handleAddBookmark(data: any) {
  try {
    const Payload = data.Payload
    const bookmarkName = Payload.bookmarkName
    if (!bookmarkName)
      return
    sendMessageToWopiClient({
      MessageId: 'CallPythonScript',
      SendTime: Date.now(),
      ScriptFile: 'BookmarkOP.py',
      Function: 'InsertBookmark',
      Values: {
        params: {
          type: 'string',
          value: bookmarkName,
        },
      },
    })
  }
  catch (e) {
    console.error(e)
    createMessage.error('执行失败，请查看控制台日志！')
  }
}

/**
 * 定位书签
 * @param data
 */
function handleLocateBookmark(data: any) {
  try {
    const Payload = data.Payload
    const bookmarkName = Payload.bookmarkName
    if (!bookmarkName)
      return
    sendMessageToWopiClient({
      MessageId: 'CallPythonScript',
      SendTime: Date.now(),
      ScriptFile: 'BookmarkOP.py',
      Function: 'LocateBookmark',
      Values: {
        params: {
          type: 'string',
          value: bookmarkName,
        },
      },
    })
  }
  catch (e) {
    console.error(e)
    createMessage.error('执行失败，请查看控制台日志！')
  }
}

/**
 * 删除指定名称的书签
 * @param data
 */
function handleDeleteBookmark(data: any) {
  try {
    const Payload = data.Payload
    const bookmarkName = Payload.bookmarkName
    if (!bookmarkName)
      return
    sendMessageToWopiClient({
      MessageId: 'CallPythonScript',
      SendTime: Date.now(),
      ScriptFile: 'BookmarkOP.py',
      Function: 'DeleteBookmark',
      Values: {
        params: {
          type: 'string',
          value: bookmarkName,
        },
      },
    })
  }
  catch (e) {
    console.error(e)
    createMessage.error('执行失败，请查看控制台日志！')
  }
}

/**
 * 使用二维数组 + 任意标签名称给表格插入行
 * @param data
 */
function handleInsertRowWithJSON(data: any) {
  try {
    const Payload = data.Payload
    const bookmarkName = Payload.bookmarkName
    const startRowIndex = Payload.startRowIndex
    const dataArray = Payload.data
    if (!bookmarkName || !dataArray)
      return

    sendMessageToWopiClient({
      MessageId: 'CallPythonScript',
      SendTime: Date.now(),
      ScriptFile: 'BookmarkOP.py',
      Function: 'InsertRowWithJSON',
      Values: {
        params: {
          type: 'string',
          value: JSON.stringify({
            location_bookmark_name: bookmarkName,
            data: dataArray,
            start_row_index: startRowIndex,
          }),
        },
      },
    })
  }
  catch (e) {
    console.error(e)
    createMessage.error('执行失败，请查看控制台日志！')
  }
}

/**
 * 删除表格行
 * @param data
 */
function handleDeleteRowWithJSON(data: any) {
  try {
    const Payload = data.Payload
    const bookmarkName = Payload.bookmarkName
    const startRowIndex = Payload.startRowIndex
    const deleteRowCount = Payload.deleteRowCount
    if (!bookmarkName || !startRowIndex)
      return

    sendMessageToWopiClient({
      MessageId: 'CallPythonScript',
      SendTime: Date.now(),
      ScriptFile: 'BookmarkOP.py',
      Function: 'DeleteRowWithJSON',
      Values: {
        params: {
          type: 'string',
          value: JSON.stringify({
            location_bookmark_name: bookmarkName,
            start_row_index: startRowIndex,
            delete_row_count: deleteRowCount,
          }),
        },
      },
    })
  }
  catch (e) {
    console.error(e)
    createMessage.error('执行失败，请查看控制台日志！')
  }
}

/**
 * 第三方主动调用，获取文件是否被修改
 */
function handleSendModifyStatus() {
  sendMessageToCaller({
    ActionId: 'ModifyStatus',
    Payload: {
      modify: editStatus.value.Modified,
    },
    Timestamp: Date.now(),
  }, true)
}

/**
 * 文件加载完成
 */
function handleDocumentReady(data: any) {
  const Values = data?.Values
  const Status = Values?.Status as string
  if (Status === 'Document_Loaded') {
    sendMessageToCaller({
      ActionId: 'DocumentLoaded',
      Payload: null,
      Timestamp: Date.now(),
    }, true)
  }
}

function handleReplaceTextAndInsertTableRow(data: any) {
  const Payload = data.Payload
  const text = Payload.text
  const table = Payload.table
  const tableData: any[] = []
  if (table) {
    for (const tab_obj of table) {
      tableData.push({
        location_bookmark_name: tab_obj.bookmarkName,
        start_row_index: tab_obj.startRowIndex,
        data: tab_obj.data,
      })
    }
  }
  sendMessageToWopiClient({
    MessageId: 'CallPythonScript',
    SendTime: Date.now(),
    ScriptFile: 'BookmarkOP.py',
    Function: 'ReplaceTextAndInsertTableRow',
    Values: {
      params: {
        type: 'string',
        value: JSON.stringify({
          text,
          table: tableData,
          image: Payload?.image ? Payload?.image : [],
        }),
      },
    },
  })
}

function handleReplaceTextAndInsertTableRowWithContentControl(data: any) {
  const Payload = data.Payload
  const text = Payload.text
  const table = Payload.table
  const tableData: any[] = []
  if (table) {
    for (const tab_obj of table) {
      tableData.push({
        location_bookmark_name: tab_obj.bookmarkName,
        start_row_index: tab_obj.startRowIndex,
        data: tab_obj.data,
      })
    }
  }
  sendMessageToWopiClient({
    MessageId: 'CallPythonScript',
    SendTime: Date.now(),
    ScriptFile: 'BookmarkOP.py',
    Function: 'ReplaceTextAndInsertTableRowWithContentControl',
    Values: {
      params: {
        type: 'string',
        value: JSON.stringify({
          text,
          table: tableData,
          image: Payload?.image ? Payload?.image : [],
        }),
      },
    },
  })
}

const callCount = ref(0)

function handleNextEditableZone() {
  sendMessageToWopiClient({
    MessageId: 'CallPythonScript',
    SendTime: Date.now(),
    ScriptFile: 'BookmarkOP.py',
    Function: 'NextEditableZone',
    Values: {
      params: {
        type: 'string',
        value: callCount.value.toString(),
      },
    },
  })
  callCount.value++
}

function logEvent(e: MessageEvent) {
  console.log('=============receive message start=======')
  console.log(typeof e.data)
  console.log(e.data)
  console.log(e.origin)
}
</script>

<template>
  <div>
    <div class="flex flex-col" :class="{ 'p-2 pt-4': !isEmbed }">
      <div class="w-full flex flex-row" :class="{ 'h-[calc(100vh)]': isEmbed, 'h-[calc(100vh-105px)]': !isEmbed }">
        <div class="w-full flex flex-row bg-white dark:bg-black">
          <i-frame v-if="previewUrl" ref="iframeRef" class="w-full bg-white dark:bg-black" :src="previewUrl" :height="isEmbed ? 'calc(100vh)' : 'calc(100vh) - 105px'" />
        </div>
      </div>
    </div>
  </div>
</template>
