import sha256 from 'crypto-js/sha256'
import {
  subscribe,
  unsubscribe,
  publish,
  createClient,
  decodePayload,
  sm4
} from '@/utils/mqtt'

export function createProxy (sn, onMessage, onClose) {
  const snHash = sha256(sn).toString().toUpperCase()

  let timer = -1
  let fn

  const targetTopic = `smsb/${snHash}/init/reply`

  const promise = new Promise((resolve, reject) => {
    fn = (topic, payload) => {
      if (topic === targetTopic) {
        unsubscribe(targetTopic, fn)
        try {
          onMessage(topic, payload)
          createProxyClient(JSON.parse(payload), { onMessage, onClose, resolve, reject })
        } catch (e) {
          reject()
        }
      }
    }

    timer = setTimeout(() => {
      publish(`smsb/${snHash}/init`, getPayload(), true).catch(reject)
    }, 500)

    subscribe(targetTopic, fn)
  })

  promise.reject = () => {
    clearTimeout(timer)
    unsubscribe(targetTopic, fn)
  }

  return promise
}

function createProxyClient ({ productId, deviceId, username, password }, { onMessage, onClose, resolve, reject }) {
  console.log('Simulate Connecting mqtt client...')

  let client = createClient({
    username,
    password,
    clientId: deviceId,
    reconnectPeriod: 0
  })

  let onEnd = reject
  let force = false

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

  client.on('connect', () => {
    console.log('Simulate MQTT connected')
    client.subscribe([
      `${productId}/${deviceId}/oss/reply`
    ])
    resolve({
      productId,
      deviceId,
      stop () {
        if (!client) {
          return
        }
        force = true
        client.unsubscribe([
          `${productId}/${deviceId}/oss/reply`
        ])
        client.end(true)
      },
      publish (topic, message, encode = true, useRoot = false) {
        if (!client) {
          return
        }
        topic = `${productId}/${deviceId}${topic}`
        const payload = getPayload(message, deviceId)
        onMessage(topic, payload)
        if (useRoot) {
          publish(topic, payload, encode).catch(console.log)
        } else {
          client.publish(topic, encode ? sm4.encrypt(payload) : payload)
        }
      }
    })
    onEnd = onClose
  })

  client.on('offline', () => {
    console.log('Simulate MQTT offline')
  })

  client.on('close', () => {
    console.log('Simulate MQTT closed')
    !force && client.end(true)
  })

  client.on('end', () => {
    console.log('Simulate MQTT end')
    client = null
    !force && onEnd()
  })

  client.on('message', (topic, payload) => {
    console.log('Simulate MQTT topic', topic)
    onMessage(topic, decodePayload(topic, payload))
  })
}

function getPayload (payload, deviceId = 'init') {
  return JSON.stringify({
    messageId: `simulate_${deviceId}_${Math.random().toString(16).slice(2)}`,
    timestamp: `${Date.now()}`,
    ...payload
  })
}
