import { getAssets } from '@/api/asset'
import store from '@/store'
import {
  State,
  AssetType
} from '@/constant'
import {
  createClient,
  decodePayload,
  sm4
} from '@/utils/mqtt'

const NAMESPACE = 'asset'

export const Topic = {
  PRELOAD: 'preload',
  DEL: 'delete',
  PULL: 'pull',
  PLAY: 'play',
  STOP: 'stop',
  DOWNLOAD: 'download'
}

export const MediaType = {
  IMAGE: 1,
  VIDEO: 2,
  WEB: 3
}

export const MediaStatus = {
  ERROR: -1,
  WAITING: 0,
  LOADING: 1,
  LOADED: 2
}

export function takeOver (device, { onMessage, onCreated, onClose, debug }) {
  const { id, productId } = device

  debug('正在连接...')

  let force = false

  const prefix = `${productId}/${id}/${NAMESPACE}`

  let client = createClient({
    clientId: `${store.getters.account}_takeover_${id}`,
    reconnectPeriod: 0,
    will: {
      topic: `${prefix}/${Topic.STOP}`,
      payload: createPayload(createMessageId()),
      qos: 2,
      retain: false
    }
  })

  const messageMap = new Map()

  const onMqttMessage = (event, paylaod) => {
    const needMessageId = event !== Topic.DOWNLOAD
    const messageId = paylaod.messageId
    if (needMessageId && !messageMap.has(messageId)) {
      return
    }
    console.log('Take Over MQTT', event, paylaod)
    const requestPayload = needMessageId ? messageMap.get(messageId) : null
    needMessageId && messageMap.delete(messageId)
    switch (event) {
      case Topic.PRELOAD:
        debug('预加载资源接收成功')
        break
      case Topic.DEL:
        debug('删除资源成功')
        break
      case Topic.PULL:
        debug('资源拉取成功')
        break
      case Topic.PLAY:
        debug(`播放${requestPayload.asset.name}成功`)
        break
      case Topic.STOP:
        debug('结束')
        break
      case Topic.DOWNLOAD:
        if (!paylaod.complete) {
          return
        }
        debug('有资源状态更新')
        break
      default:
        return
    }
    onMessage(event, paylaod, requestPayload)
  }

  const mock = {
    do (event, messageId, payload) {
      this[event](messageId, payload)
    },
    preload (messageId, payload) {
      onMqttMessage(Topic.PRELOAD, { messageId })
      setTimeout(() => {
        payload.assets.forEach(asset => {
          if (asset.type !== MediaType.WEB) {
            onMqttMessage(Topic.DOWNLOAD, {
              messageId: createMessageId,
              asset,
              complete: true,
              success: [true, false][Math.random() * 2 | 0]
            })
          }
        })
      }, 2000)
    },
    delete (messageId) {
      onMqttMessage(Topic.DEL, { messageId, failures: [] })
    },
    pull (messageId) {
      getAssets({ pageNum: 1, pageSize: 10, type: [AssetType.IMAGE, AssetType.VIDEO][Math.random() * 2 | 0], status: State.RESOLVED }).then(({ data }) => {
        onMqttMessage(Topic.PULL, {
          messageId,
          assets: data.map(({ type, originalName, keyName, size, md5 }) => {
            return {
              type,
              name: originalName,
              keyName,
              size,
              md5,
              status: [MediaStatus.ERROR, MediaStatus.LOADING, MediaStatus.LOADED, MediaStatus.WAITING][Math.random() * 3 | 0]
            }
          })
        }, true, true)
      })
    },
    play (messageId) {
      onMqttMessage(Topic.PLAY, { messageId })
    },
    stop (messageId) {
      onMqttMessage(Topic.STOP, { messageId })
    }
  }

  const send = (event, paylaod) => {
    const messageId = createMessageId()
    messageMap.set(messageId, paylaod)
    if (__DEV__) {
      mock.do(event, messageId, paylaod)
    } else {
      client.publish(`${prefix}/${event}`, createPayload(messageId, paylaod))
    }
  }

  client.on('error', e => {
    console.log('Take Over MQTT error: ', e)
  })

  client.on('connect', () => {
    console.log('Take Over MQTT connected')
    debug('连接成功')
    client.subscribe(`${prefix}/+/#`)
    onCreated(createProxy(device, send, () => {
      force = true
      client.end(true)
    }, debug))
  })

  client.on('close', () => {
    console.log('Take Over MQTT closed')
    if (!force) {
      debug('连接发生异常，已断开')
      client.end(true)
    }
  })

  client.on('end', () => {
    console.log('Take Over MQTT end')
    client = null
    messageMap.clear()
    force && debug('连接已断开')
    onClose()
  })

  client.on('message', (topic, payload) => {
    const result = /^\d+\/\d+\/asset\/(.+)\/reply$/.exec(topic)
    if (result) {
      onMqttMessage(result[1], JSON.parse(decodePayload(null, payload)))
    }
  })

  return client
}

function createMessageId () {
  return `${store.getters.account}_${Math.random().toString(16).slice(2)}`
}

function createPayload (messageId, payload) {
  return sm4.encrypt(JSON.stringify({
    messageId,
    timestamp: `${Date.now()}`,
    ...payload
  }))
}

function createProxy (device, send, close, debug) {
  const { wide, high } = device

  return {
    close,
    send,
    preload (assets) {
      debug('预加载资源...')
      send(Topic.PRELOAD, { assets })
    },
    del (assets) {
      debug('删除资源...')
      send(Topic.DEL, { assets })
    },
    pull () {
      debug('资源拉取...')
      send(Topic.PULL)
    },
    play (asset, rect) {
      debug(`播放${asset.name}...`)
      send(Topic.PLAY, {
        asset,
        top: 0,
        left: 0,
        width: wide,
        height: high,
        ...rect
      })
    },
    stop () {
      debug('结束播放...')
      send(Topic.STOP)
    }
  }
}
