import mqtt from 'precompiled-mqtt'
import { getMqttOnlineClient } from '@/api/training/mqtt'
import { testConnectDeviceApi } from '@/api/training/device'
import { updateStepApi, editorTrainApi, saveTrainScoreApi } from '@/api/training/train'
export default {
  data() {
    return {
      options: {
        // clientId: 'HongHao_web_train_monitor',
        clean: true
      },
      timer: {},
      client: null,
      topics: {
        control: 'RongHui/Manager/TrainControl',
        upOnline: '$SYS/brokers/+/clients/+/connected',
        offLine: '$SYS/brokers/+/clients/+/disconnected',
        operation: 'RongHui/+/+/TrainOper'
      },
      online_client: [],
      trainStepMesgList: []

    }
  },
  mounted() {
    this.initMqtt()
  },
  // 断开连接
  beforeDestroy() {
    // console.log(this.client.end, 'aaaaaaaaaaaaaaaaa')
    // this.client && this.client.end(true)
    // this.client = null
  },
  deactivated() {
    this.client && this.client.end(true)
    this.$message.closeAll()
    this.client = null
  },
  methods: {
    initMqtt() {
      this.$message({
        showClose: false,
        duration: 0,
        dangerouslyUseHTMLString: true,
        message: '<i class="el-icon-loading"></i> MQTT服务器连接中'
      })
      //  连接至MQTT服务器
      const client = mqtt.connect(MqttAddres, this.options)
      this.client = client
      //  链接MQTT服务器成功后的回调
      client.on('connect', (e) => {
        // 获取所有在线设备
        this.getOnlineClients()

        // 链接成功后订阅主题
        this.subscribe(Object.values(this.topics))
        // 监听消息处理
        client.on('message', (topic, message) => {
          this.formatMessage(topic, message)
        })
        this.$message.closeAll()
        this.$message.success('MQTT服务器连接成功')
      })
      // 链接错误处理
      client.on('error', error => {
        this.$message.error('MQTT服务器连接失败')
        console.log('连接失败:', error)
      })
      // 重连处理
      client.on('reconnect', error => {
        this.$message.closeAll()
        console.log('正在重连:', error)
        this.$message({
          showClose: false,
          duration: 0,
          dangerouslyUseHTMLString: true,
          message: '<i class="el-icon-loading"></i> MQTT服务器连接失败，重新连接中'
        })
      })
    },
    /**
     *
     * @param {string} topic  主题
     * @param {object} message 消息内容
     */
    formatMessage(topic, message) {
      console.log(`主题: ${topic}\n\n消息:${message}`)
      if (topic.includes('connected') || topic.includes('disconnected')) {
        this.getOnlineClients()
      }
      if (topic.includes('TrainOper')) {
        this.judge(message)
      }
    },
    /**
     * 判断硬件传来的学校步骤操作的信息
     * @param {object} stepInfo
     */
    async judge(stepInfo) {
      const messageBox = document.getElementById("messageList");
      const stepBox = document.querySelectorAll(".stepInfoBox");
      stepBox?.forEach((item) => {
        let ele = item.querySelector(".step-item-pending");
        console.log(ele);
        ele && ele.scrollIntoView();
      });
      messageBox?.scrollTo(0, messageBox.scrollHeight);

      if (this.trainInfo.status !== 1) {
        return
      }
      if (this.trainInfo.suspend) {
        this.$notify.error({
          title: '错误',
          message: '训练暂停中，但收到用户操作信息！'
        })
        return
      }
      const data = JSON.parse(stepInfo.toString())
      const { ComponentStatus, CreateTime, GroupID, IP, OperComponent, OperDevice, UserID } = JSON.parse(stepInfo.toString())
      // if(ComponentStatus!==)
      if (this.groupInfo[GroupID].isOver) {
        // this.$notify.error({
        //   title: '错误',
        //   message: `第${GroupID}组训练已结束，但收到用户操作数据！`
        // })
        return
      }
      // if (await this.checkTrainIsOver()) return
      let limitTime
      if (this.groupInfo[GroupID].nowStep) {
        let { duration, delay } = this.groupInfo[GroupID].nowStep.stepInfo
        limitTime = duration * 1000 + delay * 1000
      } else {
        this.checkTrainIsOver()
        return
      }
      const { partInfo, deviceInfo, operationInfo } = this.groupInfo[GroupID].nowStep
      const elapsedTime = this.groupInfo[GroupID].prevStepTime ? new Date().getTime() - this.groupInfo[GroupID].prevStepTime - this.groupInfo[GroupID].pause : 0
      // console.log(new Date(CreateTime).getTime(), this.prevStepTime, this.nowTrainStep.pause)
      this.groupInfo[GroupID].nowStep.duration = elapsedTime
      console.log('训练消息----↓');
      console.log(ComponentStatus, CreateTime, GroupID, IP, OperComponent, OperDevice, UserID);
      console.log('训练消息----↑');

      // debugger
      if (OperDevice !== deviceInfo.keycode || ComponentStatus !== operationInfo.keycode || OperComponent !== partInfo.keycode) {
        this.formatStepMessage(data, 'error')
        this.$set(this.groupInfo[GroupID].nowStep, 'result', 2)
        this.upLoadStepInfo(JSON.parse(JSON.stringify(this.groupInfo[GroupID].nowStep)))
        this.publish({ topic: 'RongHui/Manager/TrainSetp', payload: JSON.stringify({ ...data, OperResult: 0 }) })
      } else {
        console.log(elapsedTime, limitTime, '总共的时间有多少');
        if (elapsedTime > limitTime) {
          data.duration = (elapsedTime - limitTime) / 1000
          this.formatStepMessage(data, 'timeout')
          this.$set(this.groupInfo[GroupID].nowStep, 'result', 2)
          this.publish({ topic: 'RongHui/Manager/TrainSetp', payload: JSON.stringify({ ...data, OperResult: 2 }) })
        } else {
          this.formatStepMessage(data, 'correct')
          this.$set(this.groupInfo[GroupID].nowStep, 'result', 1)
          this.publish({ topic: 'RongHui/Manager/TrainSetp', payload: JSON.stringify({ ...data, OperResult: 1 }) })
        }
        // this.setStepTimeLine(GroupID, { status: '等待用时', value: 1 })
        // 设置当前展示的时间状态
        this.upLoadStepInfo(JSON.parse(JSON.stringify(this.groupInfo[GroupID].nowStep)))
        this.groupInfo[GroupID].nowStep.status = 1
        // this.groupInfo[GroupId].completeStepCount++
        if (!this.checkTrainIsOver()) {
          let nextStep = {}
          this.groupInfo[GroupID].nowStepIndex++
          let index = this.groupInfo[GroupID].nowStepIndex
          nextStep = this.groupInfo[GroupID]?.steps[index]
          nextStep && (nextStep.status = 3)
          this.groupInfo[GroupID].prevStepTime = new Date().getTime()
          this.groupInfo[GroupID].nowStep = nextStep
          this.sendRobotStepInfo(GroupID, nextStep)
          this.setStepTimeLine(GroupID, { status: '等待用时', value: 0 })
        }
      }
    },
    upLoadStepInfo({ id, result, pause, duration }) {
      updateStepApi(id, { status: result, pause, duration })
      // console.log(stepInfo, '待上传的步骤数据')
    },
    // 设置训练步骤的进度条
    setStepTimeLine(GroupID, value) {
      this.timer[GroupID] && clearInterval(this.timer[GroupID])
      let step = this.groupInfo[GroupID].nowStep
      if (!step) return
      if (!value) return step.timeline = null

      if (!step.timeline) {
        this.$set(step, 'timeline', { ...value, count: 0.1, awaitTime: 0 })
      }
      if (this.trainInfo.status === 1) {
        let value = 0
        let count = step.timeline.count
        let timer = setInterval(() => {
          count += 0.2
          if (step.timeline.status === '等待用时') {
            value = count / step.stepInfo.delay
            step.timeline.awaitTime = step.stepInfo.delay
            if (value > 1) {
              step.timeline.value = 0
              count = 0
              step.timeline.count = 0
              step.timeline.status = '操作用时'
              return
            }
          } else {
            value = count / step.stepInfo.duration
            step.timeline.awaitTime = step.stepInfo.duration
            console.log(value);
            if (value > 1) {
              step.timeline.status = '已超时'
              count = 0
              step.count = 0
              clearInterval(timer)
              return
            }
          }
          step.timeline.count = count
          step.timeline.value = value > 1 ? 100 : Math.round(value * 100)
        }, 200);
        this.timer[GroupID] = timer
      }
    },
    checkTrainIsOver(manualOver) {
      let isOver = true
      for (const key in this.groupInfo) {
        const base = this.groupInfo[key]
        // console.log(base.nowStepIndex + 1, base.steps.length)
        if (base.nowStepIndex + 1 < base.steps.length) {
          isOver = false
        }
      }
      if (isOver || manualOver) {
        Object.keys(this.groupInfo).forEach(key => {
          this.setStepTimeLine(key)
        })
        this.$alert('当前训练已结束，即将前往统计页面，请勿刷新页面或随意点击！', '训练结束', {
          confirmButtonText: '确定',
          callback: async action => {
            this.$message({
              type: 'info',
              message: '正在整理数据进行跳转，请勿刷新！'
            })
            const needUpLoadDate = await this.calculateGrade()

            console.log(needUpLoadDate, 'aaaaaaaa');
            // 判断是否需要人工评分  有返回数据则不需要
            if (needUpLoadDate) {
              await saveTrainScoreApi(this.trainInfo.id, needUpLoadDate)
              this.$router.push({ path: '/train/training?step=3', query: { id: this.trainInfo.id } })
              location.reload()
            } else {
              for (let key in this.groupInfo) {
                const base = this.groupInfo[key];
                base.usersArray.forEach((item) => {
                  item.stepScore = parseInt(item.stepScore * 0.8);
                });
              }
              this.trainInfo.isShowCalculate = true
            }
            await editorTrainApi(this.trainInfo.id, { status: 3 })
          }
        })
      }
      // console.log(isOver)
      return isOver
    },
    /*
     * 格式化训练消息，用于在训练判读数据中展示
     * @param {object} message 训练消息
     * @param {string} status 消息的状态 error | timeout | correct
     */
    formatStepMessage(message, status) {
      const device = this.trainDeviceInfo.find(item => item.keycode === message.OperDevice)
      let part = null
      let operation = null
      let user = null
      const GroupId = message.GroupID
      this.trainUserInfo.forEach(userInfo => {
        if (userInfo.userInfo.id === message.UserID) {
          user = userInfo.userInfo.name
        }
      })
      if (device) {
        part = device.part.find(item => item.keycode === message.OperComponent)
        if (part) {
          operation = part.operation.find(item => item.keycode === message.ComponentStatus)
        }
      }

      const mesInfo = { status }
      switch (status) {
        case 'correct':
          mesInfo.content = `步骤${this.groupInfo[GroupId].nowStep.stepInfo.sort}：操作正确`
          break
        case 'timeout':
          mesInfo.content = `步骤${this.groupInfo[GroupId].nowStep.stepInfo.sort}：操作正确 失误详情：超时${message.duration}秒`
          break
        case 'error':
          mesInfo.content = `步骤${this.groupInfo[GroupId].nowStep.stepInfo.sort}：操作错误 `
          mesInfo.info = `训练人员【${user || '未知'}】，${operation?.name || '未知'}【${device?.name || '未知'}】设备，【${part?.name || '未知'}】按钮`
          break
        default:
          break
      }
      if (this.groupInfo[GroupId].messageList) {
        this.groupInfo[GroupId].messageList.push(mesInfo)
      } else {
        this.groupInfo[GroupId].messageList = [mesInfo]
      }

      this.$nextTick(() => {
        const messageBox = document.getElementById('messageList')
        const stepBox = document.querySelectorAll('.stepInfoBox')
        stepBox?.forEach(item => {
          let ele = item.querySelector('.step-item-pending')
          console.log(ele);
          ele && ele.scrollIntoView();
        })
        messageBox?.scrollTo(0, messageBox.scrollHeight)
      })

    },
    // 获取在线设备列表
    async getOnlineClients() {
      try {
        if (!this.client?.connected) return
        let { data } = await getMqttOnlineClient()
        let keyCodes = [].concat(...(this.trainInfo.settings || []))?.map(item => {
          return item.deviceInfo.keycode + '-' + item.ip.ip
        })
        console.log(data);
        this.online_client = data.data
        data = data.data.filter(item => {
          let clientid = item.clientid.split('-')
          item.clientid = [clientid[0], clientid[1]].join('-')
          return keyCodes.includes(item.clientid)
        })
        keyCodes = data.map(item => {
          return item.clientid
        });
        [].concat(...this.trainInfo.settings || []).forEach(item => {
          let ip = item.deviceInfo.keycode + '-' + item.ip.ip
          if (keyCodes.includes(ip)) {
            this.$set(item, 'alive', true)
          } else {
            this.$set(item, 'alive', false)
          }
        })
        const ips = data.map(item => item.ip_address)
        // 调用获取设备延迟方法
        this.getDeviceDelay(ips)
      } catch (error) {
        this.$message.error('在线设备列表获取失败')
        console.log('在线设备列表获取失败：', error)
      }
    },
    // 训练结束后计算成绩
    calculateGrade() {
      const waitUploadData = []
      const waitSendData = []
      for (const key in this.groupInfo) {
        const base = this.groupInfo[key]
        // 获取普通学员
        const studentList = base.usersArray.filter(item => {
          return item.deviceInfo.rule === 2
        })
        // 组长只可以有一个
        const groupLeader = base.usersArray.find(item => item.deviceInfo.rule === 1)
        // 处理学员分数
        studentList.forEach((user) => {
          user.ruleInfo = this.trainInfo.rule.automaticScoring.find(item => item.id === user.deviceInfo.rule)
          console.log(user.step);
          if (user.step) {
            const steps = user.step
            steps.forEach(item => {
              item.status = item.result
            })
            const errList = steps.filter(item => item.status === 2)
            const stepScore = parseInt(100 - 100 / (steps.length || 0) * (errList?.length || 0) * user.ruleInfo.percentage)
            console.log(stepScore, '分数');
            this.$set(user, 'stepScore', parseInt(stepScore))
            const duration = steps.reduce((prev, cur) => {
              return prev + +cur.duration
            }, 0)
            waitUploadData.push({ train: this.trainInfo.id, setting: user.deviceInfo.id, score: stepScore, duration })
            waitSendData.push({
              UserID: user.id,
              JobId: user.deviceInfo.deviceInfo.position,
              GroupID: key,
              JobDevice: user.deviceInfo.deviceInfo.keycode,
              Score: stepScore,
              TotalStep: user.step.length,
              ErrorStep: errList.length,
              CorrectStep: user.step.length - errList.length,
              Ip: user.deviceInfo.ip.ip,

            })
          } else {
            this.$set(user, 'stepScore', 100)
            waitUploadData.push({ train: this.trainInfo.id, setting: user.deviceInfo.id, duration: 0, score: 100 })
            waitSendData.push({
              UserID: user.id,
              JobId: user.deviceInfo.deviceInfo.position,
              GroupID: key,
              JobDevice: user.deviceInfo.deviceInfo.keycode,
              Score: 100,
              TotalStep: 0,
              ErrorStep: 0,
              CorrectStep: 0,
              Ip: user.deviceInfo.ip.ip,
            })
          }
        })
        if (!groupLeader) continue
        groupLeader.ruleInfo = this.trainInfo.rule.automaticScoring.find(item => item.id === groupLeader?.deviceInfo.rule)
        this.$set(groupLeader, 'stepScore', 0)
        // 处理组长分数
        groupLeader && groupLeader.ruleInfo?.percentageList.forEach((item, index) => {
          const stepScore = studentList[index].stepScore * item.percentage
          groupLeader.stepScore += stepScore
        })
        groupLeader.stepScore > 100 && (groupLeader.stepScore = 100)
        groupLeader.stepScore = parseInt(groupLeader.stepScore)
        const duration = groupLeader.step.reduce((prev, cur) => {
          return prev + cur.duration
        }, 0)
        let errList = groupLeader.step?.find(item => {
          return item.status === 2
        }) || []
        waitUploadData.push({ train: this.trainInfo.id, setting: groupLeader.deviceInfo.id, duration, score: 100 })
        waitSendData.push({
          UserID: groupLeader.id,
          JobId: groupLeader.deviceInfo.deviceInfo.position,
          GroupID: key,
          JobDevice: groupLeader.deviceInfo.deviceInfo.keycode,
          Score: groupLeader.stepScore,
          TotalStep: groupLeader.step?.length || 0,
          ErrorStep: errList.length,
          CorrectStep: groupLeader.step ? groupLeader.step.length - errList.length : 0,
          Ip: groupLeader.deviceInfo.ip.ip,
        })
      }
      if (this.trainInfo.rule.judgmentShape === 1) {
        console.log(waitSendData);
        this.trainInfo.waitSendData = waitSendData
        this.sendOverData(waitSendData)
        return waitUploadData
      }


    },
    // 获取设备延迟
    async getDeviceDelay(ips) {
      if (!this.trainInfo.settings) return
      // 发起请求像后端获取当前链接MQTT的客户端延迟
      const resList = await Promise.all(ips.map(ip => testConnectDeviceApi({ host: ip }).then(res => res.data)))
      // 获取所有延迟
      const devlist = [].concat(...this.trainInfo.settings)
      // 根据参与训练的设备去请求结果中获取对应的延迟
      devlist.forEach(dev => {
        const resInfo = resList.find(res => dev.ip.ip === res.host)
        this.$set(dev, 'delay', resInfo?.alive ? Math.ceil(resInfo.avg) : 0)
      })
      // console.log(devlist)
      // console.log(resList)
    },
    // 开始训练
    startTranin() {
      const JobSet = []
      for (const k in this.groupInfo) {
        const base = this.groupInfo[k]
        const groupInfo = {
          GroupId: k,
          Devices: base.devicesArray.map(device => {
            return {
              DeviceKey: device.deviceInfo.keycode,
              DeviceIP: device.ip.ip,
              User: {
                ID: device.userInfo.id,
                Name: device.userInfo.name,
                Gender: device.userInfo.sex
              }
            }
          })
        }
        JobSet.push(groupInfo)
        this.setStepTimeLine(k, { status: '等待用时', value: 0 })
      }
      const payload = {
        ID: this.trainInfo.id,
        Status: 1,
        Ip: location.hostname,
        CreateTime: new Date().format('yyyy-MM-dd hh:mm'),
        Body: {
          Name: this.trainInfo.name,
          EnableScreenRecord: 0,
          Duration: +this.trainInfo.duration,
          Type: this.trainInfo.type,
          EnableGuide: this.trainInfo.guide,
          JobSet
        }
      }
      let deviceNeedData = [];
      for (const key in this.groupInfo) {
        this.groupInfo[key].prevStepTime = new Date().getTime()
      }
      let sendStartMessage = async () => {
        this.trainInfo.status = 1;
        await editorTrainApi(this.trainInfo.id, { status: 1 });
        this.publish({ topic: this.topics.control, payload: JSON.stringify(payload) })
        Object.keys(this.groupInfo).forEach(groupId => {
          this.sendRobotStepInfo(groupId, this.groupInfo[groupId].steps[0])
        })
      }
      this.$confirm("是否开启训练直播？", "提示", {
        confirmButtonText: "开启",
        cancelButtonText: "不开启",
        type: "warning",
      })
        .then(async () => {
          payload.Body.EnableScreenRecord = 1
          sendStartMessage()
        }).catch(_ => {
          payload.Body.EnableScreenRecord = 0
          sendStartMessage()
        })

      // this.prevStepTime = new Date().getTime()
    },
    sendRobotStepInfo(groupId, step) {
      console.log(step, '机器人步骤信息');
      try {
        if (step?.userInfo.id === '0') {
          setTimeout(() => {
            if (this.trainInfo.suspend) return
            let { operationInfo, deviceInfo, partInfo, stepInfo, comment } = step
            let publishData = {
              OperComponent: partInfo.keycode,
              Tag: stepInfo.comment,
              Interval: stepInfo.duration,
              ComponentStatus: operationInfo.keycode,
              Ip: location.hostname,
              CreateTime: new Date().format('yyyy-MM-dd hh:mm'),
            }
            this.publish({ topic: `RongHui/${groupId}/${deviceInfo.keycode}/Robot`, payload: JSON.stringify(publishData) })
          }, step.stepInfo.delay * 1000);
        }
      } catch (error) {
        console.log(error);
        this.$message.error('机器人步骤信息处理错误')
      }
    },
    // 结束训练
    sendOverData(data) {
      console.log('训练结束后的统计数据：', data,);
      const payload = {
        ID: this.trainInfo.id,
        Status: 0,
        Ip: location.hostname,
        CreateTime: new Date().format('yyyy-MM-dd hh:mm'),
        Body: data
      }
      this.publish({ topic: this.topics.control, payload: JSON.stringify(payload) })
    },
    /**
     *
     * @param {number} status
     * status: 0=结束训练 2=暂停训练  3=恢复训练
     */
    pauseResumeEnd(status) {
      const payload = {
        ID: this.trainInfo.id,
        IP: window.location.hostname,
        Status: status,
        CreateTime: new Date(),
        Body: null
      }
      this.publish({ topic: this.topics.control, payload: JSON.stringify(payload) })
    },
    /**
     * 像MQTT服务器发送消息
     * @param {string} param0.topic
     * @param {any} param0.payload
     */
    publish({ topic, payload }) {
      this.client.publish(topic, payload, 0, error => {
        if (error) {
          this.$message.error('与MQTT服务器通讯失败')
          console.log('Publish error', error)
        }
      })
    },
    /**
   * 订阅主题
   * @param {Array<string>} topic
   */
    subscribe(topic) {
      this.client.subscribe(topic, error => {
        if (!error) {
          this.$message.success('主题订阅成功')
        } else {
          console.log('订阅失败')
        }
      })
    }
  }
}
