import { useWebSocket } from '@vueuse/core'
import { userStore, globalStore, urgeStore } from '@/stores'
import type { UseWebSocketReturn } from '@vueuse/core'
import { getSN, $XXL, isNotStudyMachine } from '@/utils'
import { useLog, LogType, useTips } from '@/hooks'
import { useRouter } from 'vue-router'
import { storeToRefs } from 'pinia'
import {
  handleClassMonitorMessage,
  resolveMonitorWsType,
  stopClassMonitor,
} from './useClassMonitor'
import { uploadNativeFilePathToCos } from '@/utils/cosUpload'

let SOCKET: UseWebSocketReturn<any>

enum SOCKET_MSG_TYPE {
  lockScreen = 'lockScreen',
  unLockScreen = 'unLockScreen',
  desktopPreview = 'desktopPreview',
  inspectorTalkMessage = 'inspectorTalkMessage',
  inspectorStart = 'inspectorStart',
  inspectorEnd = 'inspectorEnd',
  systemMessage = 'systemMessage',
  messageReceive = 'messageReceive',
  monitorStart = 'monitorStart',
  monitorStop = 'monitorStop',
}

const inspectorEnd = ref<anyObj>()
const inspectorMessage = ref<anyObj>()

export const useSocket = () => {
  const router = useRouter()
  const { token, userInfo } = storeToRefs(userStore())
  const { system } = storeToRefs(globalStore())
  const { getInspectorInfo } = urgeStore()
  const SN = getSN()

  const init = () => {
    if (import.meta.env.MODE === 'test') return
    if (!token.value) return
    stop()
    SOCKET = useWebSocket(
      `${import.meta.env.VITE_WS_URL}?accessToken=${token.value.replace('Bearer ', '')}`,
      {
        autoReconnect: {
          retries: () => true,
          delay: 10000,
          onFailed() {
            console.log('重新连接失败')
          },
        },
        heartbeat: {
          message: '{"type":"ping"}',
          interval: 55000,
          pongTimeout: 3000,
        },
        onConnected,
        onDisconnected,
        onError,
        onMessage,
      },
    )
    SOCKET.send(
      JSON.stringify({
        type: 'ping',
      }),
    )
  }

  const onConnected = (ws: WebSocket) => {
    // ws.binaryType = 'arraybuffer';
    // console.log('ws已连接');
  }

  const onDisconnected = (ws: WebSocket, event: CloseEvent) => {
    if (!token.value) {
      stop()
      SOCKET = null as any
    }
    // console.log('ws已关闭', event);
  }

  const onError = (ws: WebSocket, event: Event) => {
    // console.log(ws, event);
  }

  const getLockFlag = (data: anyObj, fallback: 0 | 1): 0 | 1 => {
    const locked = data.msg?.locked
    if (locked === true || locked === 'true') return 1
    if (locked === false || locked === 'false') return 0

    const lockFlag = Number(locked)
    return lockFlag === 0 || lockFlag === 1 ? lockFlag : fallback
  }

  const updateLockScreen = (lockFlag: 0 | 1) => {
    if (isNotStudyMachine) return

    system.value.isLock = lockFlag === 1
    useLog(LogType.LockScreenTime, {
      simSerialNumber: SN,
      lockFlag,
    })
  }

  const onMessage = (ws: WebSocket, event: MessageEvent) => {
    const data = JSON.parse(event.data)
    const monitorEnvelope = resolveMonitorWsType(data)
    const messageType = monitorEnvelope ?? data.type ?? data.msg?.type
    const isPayloadMonitor = Boolean(monitorEnvelope)

    // 确认接收
    sendMsg({
      type: SOCKET_MSG_TYPE.messageReceive,
      msg: data.msg,
    })

    // 全局消息提醒
    if (messageType === SOCKET_MSG_TYPE.systemMessage) {
      useTips({
        tips: data.msg,
        showCancel: false,
      })
      return
    }

    // if (isWeb) return;
    if (
      !isPayloadMonitor &&
      messageType !== SOCKET_MSG_TYPE.lockScreen &&
      messageType !== SOCKET_MSG_TYPE.unLockScreen &&
      typeof data.msg?.locked !== 'undefined'
    ) {
      updateLockScreen(getLockFlag(data, 0))
      return
    }

    switch (messageType) {
      case SOCKET_MSG_TYPE.lockScreen:
        // 执行锁屏
        updateLockScreen(getLockFlag(data, 1))
        break
      case SOCKET_MSG_TYPE.unLockScreen:
        // 执行解锁
        updateLockScreen(getLockFlag(data, 0))
        break
      case SOCKET_MSG_TYPE.desktopPreview:
        if (isNotStudyMachine) return
        onDesktopPreview()
        break
      case SOCKET_MSG_TYPE.monitorStart:
      case SOCKET_MSG_TYPE.monitorStop:
        handleClassMonitorMessage(data)
        break
      case SOCKET_MSG_TYPE.inspectorStart:
        // 开始督学
        // console.log('开始督学');
        getInspectorInfo()
          .then(() => router.push('/urge/learning'))
          .catch(() => {
            const _router = useRouter()
            _router.push('/urge/learning')
          }) // 更新督学信息
        break
      case SOCKET_MSG_TYPE.inspectorEnd:
        // 结束督学
        // console.log('结束督学');
        inspectorEnd.value = data.msg
        getInspectorInfo()
          .then(() => router.push('/'))
          .catch(() => {
            const _router = useRouter()
            _router.push('/')
          }) // 更新督学信息
        break
      case SOCKET_MSG_TYPE.inspectorTalkMessage:
        inspectorMessage.value = data.msg
        break
    }
  }

  // 桌面预览
  const onDesktopPreview = async () => {
    try {
      const { data: filePath } = $XXL.getScreenshot()
      // console.log('截图本地地址', filePath);
      if (!filePath)
        return sendMsg({
          type: 'desktopScreenshotImage',
          userId: userInfo.value.userId,
          simSerialNumber: SN,
          msg: '',
        })
      const res = await uploadNativeFilePathToCos(filePath, config => $XXL.uploadFile(config), {
        fileType: 1,
      })
      sendMsg({
        type: 'desktopScreenshotImage',
        userId: userInfo.value.userId,
        simSerialNumber: SN,
        msg: res.url,
      })
    } catch (err) {
      console.log(err)
    }
  }

  // 发送消息
  const sendMsg = (data: anyObj, config = {}) => {
    if (!SOCKET) {
      // console.error('ws不存在');
      return
    }
    SOCKET.send(JSON.stringify(data))
  }

  const textToBuffer = (str: string) => {
    return new TextEncoder().encode(str)
  }

  const bufferToText = (buffer: ArrayBuffer) => {
    return new TextDecoder().decode(buffer)
  }

  const stop = () => {
    stopClassMonitor()
    SOCKET?.close()
  }

  return {
    init,
    stop,
    sendMsg,
    inspectorEnd,
    inspectorMessage,
  }
}
