import React from 'react'
import { GameLaunchStatus, mainSlice, useMainSelector } from '../model'
import { EventEmitter } from 'events'
import polyfillSDK from '../utils/polyfillSDK'
import { useDispatch } from 'react-redux'
import { useQuery } from 'react-query'
import { GetExtVersionInfoRsp } from '../typings/panel'
import logger from '../utils/logger'
import { GameEventInstance } from '../proxy/client'

const bus = new EventEmitter()
const GAME_MESSAGE = 'GAME_MESSAGE'

export const usePanelData = () => {
  const dispatch = useDispatch()

  return useQuery<void, Error>(
    ['panel-data'],
    async () => {
      const versionInfo = await polyfillSDK.getExtInfo()
      const rsp = (await polyfillSDK.commonQuery({
        key: 'getExtVersionInfo',
        param: {
          extVersion: versionInfo.extVersion,
        },
      })) as GetExtVersionInfoRsp
      if (rsp) {
        const streamerInfo = await polyfillSDK.getStreamerInfo()
        const userInfo = await polyfillSDK.getUserInfo()
        // 更新状态
        dispatch(
          mainSlice.actions.initPanel({
            data: {
              userUnionId: userInfo.userUnionId,
              streamerUnionId: streamerInfo.streamerUnionId,
              roomid: Number(streamerInfo?.streamerRoomId) || 0,
              gameTitle: rsp.extName,
              gameDesc: rsp.extDescription,
              gameIcon: rsp.extIcon,
              gameImgUrlList: rsp.extMarketSnapshots || [],
              gameLaunchParam: {
                gameUrl: rsp.cppGameinfo.packageUrl,
                gameMd5: rsp.cppGameinfo.packageMD5,
                gameExeName: rsp.cppGameinfo.programPath,
                gameName: rsp.cppGameinfo.cppGameAppName,
                env: rsp.cppGameinfo.env,
                programParams: rsp.cppGameinfo.programParams,
                exeVersion: rsp.cppGameinfo.exeVersion,
              },
              localStatus: {
                isAvailable: true,
                status: 'idle',
                statusMsg: '',
                progressList: [0, 0],
              },
              cloudStatus: {
                isAvailable: String(rsp.enableCloudGame) === '1',
                status: 'idle',
                statusMsg: '',
                progressList: [0],
              },
            },
          })
        )
      } else {
        return Promise.reject(new Error('获取小程序信息失败'))
      }
    },
    { refetchOnWindowFocus: false }
  )
}

export const usePanel = (): [
  gameTitle: string,
  gameDesc: string,
  gameIcon: string,
  isLoading: boolean,
  error: Error | null
] => {
  const gameTitle = useMainSelector((state) => state.panel.gameTitle)
  const gameDesc = useMainSelector((state) => state.panel.gameDesc)
  const gameIcon = useMainSelector((state) => state.panel.gameIcon)

  // 获取游戏信息
  const { isLoading, error } = usePanelData()

  // 监听EXE消息
  React.useEffect(() => {
    GameEventInstance.onGameMessage({
      callback: (notice) => {
        logger.log('onGameMessage', notice)
        bus.emit(GAME_MESSAGE, notice)
      },
    })
    return () => {
      GameEventInstance.offGameMessage()
    }
  }, [])

  return [gameTitle, gameDesc, gameIcon, isLoading, error]
}

export const usePanelPic = (): [gameImgUrlList: string[]] => {
  const gameImgUrlList = useMainSelector((state) => state.panel.gameImgUrlList)

  return [gameImgUrlList]
}

export const usePanelAction = (props: {
  position: 'local' | 'cloud'
}): [btnInfo: GameLaunchStatus, isBtnLocked: boolean, launch: () => void] => {
  const dispatch = useDispatch()

  const btnInfo = useMainSelector((state) =>
    props.position === 'cloud'
      ? state.panel.cloudStatus
      : state.panel.localStatus
  )
  const isBtnLocked = useMainSelector((state) =>
    props.position === 'cloud'
      ? state.panel.localStatus.status === 'launching' ||
        state.panel.localStatus.status === 'launchSuccessed'
      : state.panel.cloudStatus.status === 'launching' ||
        state.panel.cloudStatus.status === 'launchSuccessed'
  )

  const initGameEnv = React.useCallback(() => {
    return new Promise<void>((resolve, reject) => {
      // 初始化EXE环境，触发metashell下载过程
      polyfillSDK.initGameEnv({ optParams: '' })
      // 监听进度
      const handler: hyExt.ExeOnGameMessageCallback = (notice) => {
        const { name, message } = notice
        if (name === 'GameEnvEvent') {
          const { res, progress } = JSON.parse(message)
          if (Number(res) === 1) {
            // 准备中
            dispatch(
              mainSlice.actions.launching({
                data: {
                  type: props.position,
                  progressList:
                    props.position === 'local' ? [progress, 0] : [progress],
                },
              })
            )
          } else if (Number(res) === 0) {
            // 准备完毕
            bus.removeListener(GAME_MESSAGE, handler)
            dispatch(
              mainSlice.actions.launching({
                data: {
                  type: props.position,
                  progressList: props.position === 'local' ? [100, 0] : [100],
                },
              })
            )
            resolve()
          } else {
            // 其它异常
            bus.removeListener(GAME_MESSAGE, handler)
            reject(new Error(`初始化环境失败[${res}]`))
          }
        }
      }
      bus.addListener(GAME_MESSAGE, handler)
    })
  }, [props.position])

  const [
    gameUrl,
    gameMd5,
    gameExeName,
    env,
    roomid,
    programParams,
    exeVersion,
    userUnionId,
    streamerUnionId,
  ] = useMainSelector((state) => [
    state.panel.gameLaunchParam.gameUrl,
    state.panel.gameLaunchParam.gameMd5,
    state.panel.gameLaunchParam.gameExeName,
    state.panel.gameLaunchParam.env,
    state.panel.roomid,
    state.panel.gameLaunchParam.programParams,
    state.panel.gameLaunchParam.exeVersion,
    state.panel.userUnionId,
    state.panel.streamerUnionId,
  ])

  const downloadGameExe = React.useCallback(
    () =>
      new Promise<void>((resolve, reject) => {
        // 调用下载接口下载EXE
        polyfillSDK
          .downloadBatchRes([
            {
              url: gameUrl,
              md5: gameMd5,
              unzip: true,
              offline: false,
              // @ts-ignore
              tag: 'danmu_game',
            },
          ])
          .then(
            () => {
              // 下载进度变化的处理函数
              const handler = ({
                res,
                msg,
                url,
                md5,
                bytesLoaded,
                bytesTotal,
                path,
              }: {
                res: number
                msg: string
                url: string
                md5: string
                bytesLoaded: number
                bytesTotal: number
                path: string
              }) => {
                if (md5 === gameMd5) {
                  if (res === 0) {
                    // 下载完成
                    polyfillSDK.off('downloadProgress', handler)
                    dispatch(
                      mainSlice.actions.launching({
                        data: {
                          type: props.position,
                          progressList:
                            props.position === 'local' ? [100, 100] : [100],
                        },
                      })
                    )
                    resolve()
                  } else if (res === 1) {
                    // 下载中
                    dispatch(
                      mainSlice.actions.launching({
                        data: {
                          type: props.position,
                          progressList:
                            props.position === 'local'
                              ? [
                                  100,
                                  Math.floor((bytesLoaded / bytesTotal) * 100),
                                ]
                              : [Math.floor((bytesLoaded / bytesTotal) * 100)],
                        },
                      })
                    )
                  } else {
                    // 下载失败
                    polyfillSDK.off('downloadProgress', handler)
                    reject(new Error(msg || '下载失败'))
                  }
                }
              }
              // 监听下载进度变化
              polyfillSDK.on('downloadProgress', handler)
            },
            (err) => {
              reject(err)
            }
          )
      }),
    [props.position, gameUrl, gameMd5]
  )

  const launchLocal = React.useCallback(() => {
    return polyfillSDK.launchGame({
      processMode: 'local',
      md5: gameMd5,
      exeName: gameExeName,
      // TODO 不确定是否要拼接服务端下发的参数
      exeParams: `--roomid="${roomid}" --profileid="${streamerUnionId}" --userid="${userUnionId}" ${
        programParams || ''
      }`,
      optParams: '',
      // @ts-expect-error
      env,
    })
  }, [
    gameMd5,
    gameExeName,
    env,
    roomid,
    programParams,
    streamerUnionId,
    userUnionId,
  ])

  const launchCloud = React.useCallback(() => {
    return polyfillSDK.launchGame({
      processMode: 'cloud',
      md5: '',
      exeName: gameExeName,
      // TODO 不确定是否要拼接服务端下发的参数
      exeParams: `--roomid="${roomid}" --profileid="${streamerUnionId}" --userid="${userUnionId}" ${
        programParams || ''
      }`,
      optParams: JSON.stringify({ exeVersion }),
      // @ts-expect-error
      env,
    })
  }, [
    gameExeName,
    env,
    roomid,
    programParams,
    exeVersion,
    streamerUnionId,
    userUnionId,
  ])

  const launch = React.useCallback(async () => {
    await dispatch(
      mainSlice.actions.launching({
        data: {
          type: props.position,
          progressList: props.position === 'local' ? [0, 0] : [0],
        },
      })
    )
    try {
      // 初始化环境
      await initGameEnv()
      if (props.position === 'local') {
        // 本地启动，需要先下载exe到本地
        await downloadGameExe()
        // 启动
        await launchLocal()
      } else {
        // 云端启动
        await launchCloud()
      }
      await dispatch(
        mainSlice.actions.launchSuccessed({ data: { type: props.position } })
      )
      // // 调用一个SDK接口把面板隐藏起来
      // polyfillSDK.localControlPanelVisible({
      //   extType: 'pc_anchor_panel',
      //   visible: false,
      // })
    } catch (err) {
      dispatch(
        mainSlice.actions.launchFailed({
          data: {
            type: props.position,
            msg: (err as Error).message || '启动失败',
          },
        })
      )
    }
  }, [props.position, initGameEnv, downloadGameExe, launchLocal, launchCloud])

  // 处理异常
  React.useEffect(() => {
    if (btnInfo.status === 'launchSuccessed') {
      const handler = (notice: { name: string; message: string }) => {
        const { name, message } = notice
        if (name === 'ExceptionEvent') {
          logger.log('启动异常', { position: props.position, message })
          try {
            const { hostCode, hostMessage } = JSON.parse(message) as {
              hostCode: number
              hostMessage: string
            }
            if (hostCode === 20013) {
              // 启动的exe被关闭，恢复初始状态
              dispatch(
                mainSlice.actions.launchIdle({ data: { type: props.position } })
              )
            } else {
              // 把hostCode外显出来
              dispatch(
                mainSlice.actions.launchFailed({
                  data: { type: props.position, msg: `运行异常[${hostCode}]` },
                })
              )
              // 异常退出
              polyfillSDK.shutdownGame()
            }
          } catch (err) {
            logger.log('ExceptionEvent处理异常', {
              position: props.position,
              message,
            })
          }
          // 调用一个SDK接口把面板显示出来
          polyfillSDK.localControlPanelVisible({
            extType: 'pc_anchor_panel',
            visible: true,
          })
        } else if (name === 'GameMsg') {
          logger.log('游戏信息', { position: props.position, message })
          try {
            const { message: m } = JSON.parse(message) as { message: string }
            if (m === 'GameExit') {
              dispatch(
                mainSlice.actions.launchIdle({ data: { type: props.position } })
              )
              // 调用一个SDK接口把面板显示出来
              polyfillSDK.localControlPanelVisible({
                extType: 'pc_anchor_panel',
                visible: true,
              })
            }
          } catch (err) {
            logger.log('GameMsg处理异常', { position: props.position, message })
          }
        }
      }
      bus.addListener(GAME_MESSAGE, handler)
      return () => {
        bus.removeListener(GAME_MESSAGE, handler)
      }
    }
  }, [props.position, btnInfo.status])

  const isAutoLaunch = useMainSelector((state) => {
    if (props.position === 'cloud') {
      return (
        state.panel.cloudStatus.isAvailable &&
        !state.panel.localStatus.isAvailable
      )
    } else {
      return (
        !state.panel.cloudStatus.isAvailable &&
        state.panel.localStatus.isAvailable
      )
    }
  })

  React.useEffect(() => {
    if (isAutoLaunch) {
      launch()
    }
  }, [isAutoLaunch, launch])

  return [btnInfo, isBtnLocked, launch]
}
