package com.xueyi.system.digitalmans.controller;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xueyi.common.core.constant.basic.BaseConstants;
import com.xueyi.common.core.constant.basic.SecurityConstants;
import com.xueyi.common.core.constant.basic.SqlConstants;
import com.xueyi.common.core.constant.digitalman.MessageConstants;
import com.xueyi.common.core.web.result.AjaxResult;
import com.xueyi.common.core.web.result.R;
import com.xueyi.common.core.web.validate.V_A;
import com.xueyi.common.core.web.validate.V_E;
import com.xueyi.common.datasource.annotation.Master;
import com.xueyi.common.log.annotation.Log;
import com.xueyi.common.log.enums.BusinessType;
import com.xueyi.common.redis.utils.RedisUtil;
import com.xueyi.common.security.annotation.InnerAuth;
import com.xueyi.common.security.annotation.RequiresPermissions;
import com.xueyi.common.web.entity.controller.BaseController;
import com.xueyi.common.web.utils.DateUtils;
import com.xueyi.message.api.transfer.feign.RemoteTransferService;
import com.xueyi.system.api.device.domain.vo.DeviceTenantSourceMergeVo;
import com.xueyi.system.api.device.feign.RemoteDeviceTenantMergeService;
import com.xueyi.system.api.digitalmans.domain.dto.DmDigitalmanWorktimeDto;
import com.xueyi.system.api.digitalmans.domain.dto.DmManDeviceDto;
import com.xueyi.system.api.digitalmans.domain.dto.DmSyncDigitalmanDto;
import com.xueyi.system.api.digitalmans.domain.po.DmDigitalmanExtPo;
import com.xueyi.system.api.digitalmans.domain.po.DmManDevicePo;
import com.xueyi.system.api.digitalmans.feign.RemoteDigitalmanService;
import com.xueyi.system.device.service.impl.DmDeviceTenantMergeServiceImpl;
import com.xueyi.system.digitalmans.domain.dto.DmDigitalmanDto;
import com.xueyi.system.digitalmans.domain.dto.DmDigitalmanExtDto;
import com.xueyi.system.digitalmans.domain.model.DmDigitalmanExtConverter;
import com.xueyi.system.digitalmans.domain.query.DmDigitalmanExtQuery;
import com.xueyi.system.digitalmans.domain.query.DmDigitalmanQuery;
import com.xueyi.system.digitalmans.domain.query.DmManDeviceQuery;
import com.xueyi.system.digitalmans.mapper.DmManDeviceMapper;
import com.xueyi.system.digitalmans.service.IDmDigitalmanExtService;
import com.xueyi.system.digitalmans.service.IDmDigitalmanService;
import com.xueyi.system.digitalmans.service.IDmModelService;
import com.xueyi.system.digitalmans.service.IDmSkillService;
import com.xueyi.system.digitalmans.service.impl.DmManDeviceServiceImpl;
import com.xueyi.system.emcs.constant.EmcsUploadType;
import com.xueyi.system.emcs.domain.dto.DmDeviceLogFileDto;
import com.xueyi.system.emcs.domain.dto.DmExceptionLogDto;
import com.xueyi.system.emcs.domain.dto.RedisBaseDto;
import com.xueyi.system.emcs.mapper.DmDeviceLogFileMapper;
import com.xueyi.system.emcs.mapper.DmExceptionLogMapper;
import com.xueyi.system.emcs.service.BaseRedisListDataCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.Serializable;
import java.text.ParseException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 数字人基础管理 业务处理
 *
 * @author xueyi
 */
@RestController
@RequestMapping("/man")
public class DmDigitalmanController extends BaseController<DmDigitalmanQuery, DmDigitalmanDto, IDmDigitalmanService> {

    private  final static String HEART_MQTT_PREFIX = "digital-man";
    private  final static Integer REDIS_LIST_SIZE = 60;

    @Autowired
    private BaseRedisListDataCache baseRedisListDataCache;

    @Autowired
    private DmManDeviceMapper manDeviceMapper;

    @Autowired
    private DmExceptionLogMapper exceptionLogMapper;

    @Autowired
    private DmDeviceLogFileMapper deviceLogFileMapper;

    @Autowired
    private RemoteDeviceTenantMergeService remoteDeviceTenantMergeService;

    private static final Logger log = LoggerFactory.getLogger(DmDigitalmanController.class);

    @Autowired
    private DmDeviceTenantMergeServiceImpl deviceTenantMergeService;

    @Autowired
    private IDmDigitalmanExtService dmDigitalmanExtService;

    @Autowired
    private IDmDigitalmanService dmDigitalmanService;

    @Autowired
    private IDmSkillService dmSkillService;

    @Autowired
    private IDmModelService dmModelService;

    @Autowired
    private DmDigitalmanExtConverter dmDigitalmanExtConverter;

    @Autowired
    private RemoteTransferService remoteTransferService;


    @Autowired
    private RemoteDigitalmanService remoteDigitalmanService;

    @Autowired
    private RedisTemplate<String, Serializable> redisTemplate;

    /** 定义节点名称 */
    @Override
    protected String getNodeName() {
        return "数字人基础" ;
    }

    /**
     * 同步数字人信息 | 内部调用
     */
    @InnerAuth
    @GetMapping("/inner/{manCode}/{timestamp}")
    public R<DmSyncDigitalmanDto> listInner(@RequestParam(value = "manCode") String manCode, @RequestParam(value = "timestamp") String timestamp) {
        return R.ok(super.baseService.syncDigitalman(manCode,timestamp));
    }
    @InnerAuth
    @GetMapping("/inner/select-digitalman-ext")
    public R<DmDigitalmanExtPo> queryExt(@RequestParam(value = "devId") String devId) {
        return R.ok(super.baseService.selectExt(devId));
    }

    @InnerAuth
    @PostMapping("/inner/sync_ext_isonline")
    public R<Integer> syncExtIsonline(@RequestBody List<DmManDeviceDto> devices) {
        devices.forEach(item->{
            //根据设备与租户信息更新在线状态
            AjaxResult ar = remoteTransferService.getDeviceOnlineStatus(item.getDeviceId());
            DmDigitalmanExtQuery query1 = new DmDigitalmanExtQuery();
            query1.setDeviceId(item.getDeviceId());
            List<DmDigitalmanExtDto> extList = dmDigitalmanExtService.selectListByDeviceId(query1);
            if (extList != null && extList.size() > 0) {
                extList.get(0).setIsOnline(Integer.parseInt(ar.get("msg").toString()));
                dmDigitalmanExtService.update(extList.get(0));
                // 更新缓存
                DmManDeviceDto redisDto = (DmManDeviceDto)redisTemplate.opsForHash().get(MessageConstants.REDIS_GROUP_DEVICE_HEADER,item.getDeviceId());
                redisDto.setOnlineStatus(ar.get("msg").toString());
                log.info("更新缓存：{}",redisDto);
                // 删除缓存
                redisTemplate.opsForHash().delete(MessageConstants.REDIS_GROUP_DEVICE_HEADER, item.getDeviceId());
                // 插入缓存
                redisTemplate.opsForHash().putIfAbsent(MessageConstants.REDIS_GROUP_DEVICE_HEADER,item.getDeviceId(),redisDto);
            }
        });
        return R.ok();
    }
    @PostMapping("/inner/update_device_online")
    public R<Boolean> updateDeviceOnline(String deviceId) {
        super.baseService.syncRegisteredDevice(deviceId);
            // 数字人预警机制
        return null;
    }
    /**
     * 同步数字人信息 | 内部调用
     */
    @GetMapping("/inner/init/{snCode}")
    public R<Integer> initInner(@RequestParam(value = "snCode") String snCode) {


        return R.ok(super.baseService.initDigitalman(snCode));
    }

    /**
     * 查询数字人基础列表
     */
    @Override
    @GetMapping("/list")
    @RequiresPermissions(Auth.DM_DIGITALMAN_LIST)
    public AjaxResult list(DmDigitalmanQuery dmDigitalman) {

        return super.list(dmDigitalman);
    }

    /**
     * 查询数字人基础详细
     */
    @Override
    @GetMapping(value = "/{id}")
    @RequiresPermissions(Auth.DM_DIGITALMAN_SINGLE)
    public AjaxResult getInfo(@PathVariable Serializable id) {
        return super.getInfo(id);
    }

    /**
     * 查询数字人工作时间详细
     */
    @GetMapping(value = "/worktime/{id}")
    @RequiresPermissions(Auth.DM_DIGITALMAN_SINGLE)
    public AjaxResult getWorktimeInfo(@PathVariable Serializable id) {
        return success(super.baseService.selectWorktimeById(id));
    }
    /**
     * 数字人基础新增
     */
    @Override
    @PostMapping
    @RequiresPermissions(Auth.DM_DIGITALMAN_ADD)
    @Log(title = "数字人基础管理", businessType = BusinessType.INSERT)
    public AjaxResult add(@Validated({V_A.class}) @RequestBody DmDigitalmanDto dmDigitalman) {
        return super.add(dmDigitalman);
    }


    /**
     * 数字人基础修改
     */
    @PutMapping
    @RequiresPermissions(Auth.DM_DIGITALMAN_EDIT)
    @Log(title = "数字人基础管理", businessType = BusinessType.UPDATE)
    public AjaxResult edit(@Validated({V_E.class}) @RequestBody DmDigitalmanDto digitalmanDto) {
        return super.edit(digitalmanDto);
    }

    /**
     * 数字人工作时间修改
     */
    @PutMapping(value = "/worktime")
    @Log(title = "数字人基础管理-工作时间", businessType = BusinessType.UPDATE)
    public AjaxResult worktimeEdit(@Validated({V_E.class}) @RequestBody DmDigitalmanWorktimeDto customMadeDto) {
        customMadeDto.initOperate(BaseConstants.Operate.EDIT);
        return toAjax(baseService.worktimeUpdate(customMadeDto));
    }

    /**
     * 数字人基础批量删除
     */
    @Override
    @DeleteMapping("/batch/{idList}")
    @RequiresPermissions(Auth.DM_DIGITALMAN_DEL)
    @Log(title = "数字人基础管理", businessType = BusinessType.DELETE)
    public AjaxResult batchRemove(@PathVariable List<Long> idList) {
        return super.batchRemove(idList);
    }

    /**
     * 获取数字人基础选择框列表
     */
    @Override
    @GetMapping("/option")
    public AjaxResult option() {
        return super.option();
    }

    interface Auth {
        /** 系统 - 数字人基础管理 - 列表 */
        String DM_DIGITALMAN_LIST = "man:man:list";
        /** 系统 - 数字人基础管理 - 详情 */
        String DM_DIGITALMAN_SINGLE = "man:man:single";
        /** 系统 - 数字人基础管理 - 新增 */
        String DM_DIGITALMAN_ADD = "man:man:add";
        /** 系统 - 数字人基础管理 - 修改 */
        String DM_DIGITALMAN_EDIT = "man:man:edit";
        /** 系统 - 数字人基础管理 - 删除 */
        String DM_DIGITALMAN_DEL = "man:man:delete";
    }


    @PostMapping("/api/mqtt/heartbeat")
    public void heartbeat(@RequestBody(required = true) String message) {
        JSONObject heartBeatObj = JSONObject.parseObject(message);
        String devId = heartBeatObj.getString("devId");
        String timestamp = heartBeatObj.getString("timestamp");
        String network = heartBeatObj.getString("wifi_rssi");
        String cpu_max = heartBeatObj.getString("cpuinfo_max_freq");
        String cpu_scaling = heartBeatObj.getString("scaling_cur_freq");
        String ipAddr = heartBeatObj.getString("ip");
        String cpu = Double.valueOf(cpu_scaling) / Double.valueOf(cpu_max) * 100 + "";
        String memoryTotal = heartBeatObj.getString("memory_total");
        Double memoryFree = Double.valueOf(heartBeatObj.getString("memory_free"));
        String memory = (Double.valueOf(memoryTotal) - memoryFree) / Double.valueOf(memoryTotal)  * 100 + "";
        String occurTime = "";
        DmDigitalmanExtPo extPo = dmDigitalmanService.selectExt(devId);

        if (StringUtils.isNotEmpty(ipAddr)) {
            extPo.setIpAddr(ipAddr);
            dmDigitalmanExtService.update(dmDigitalmanExtConverter.mapperDto(extPo));
        }

        try {
            occurTime = DateUtils.formatDate(DateUtils.parseLongToDate(timestamp),"HH:mm");
        } catch (ParseException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        try{
        if (StringUtils.isNotEmpty(devId)) {
            if (StringUtils.isNotEmpty(network)){
                baseRedisListDataCache.init (REDIS_LIST_SIZE).addToList(HEART_MQTT_PREFIX+":"+devId+":network", new RedisBaseDto(occurTime, network));
                baseRedisListDataCache.setListExpiration(HEART_MQTT_PREFIX+":"+devId+":network", Duration.ofHours(1));

            }
            if (StringUtils.isNotEmpty(cpu)) {
                baseRedisListDataCache.init(REDIS_LIST_SIZE).addToList(HEART_MQTT_PREFIX + ":" + devId + ":cpu", new RedisBaseDto(occurTime, cpu));
                baseRedisListDataCache.setListExpiration(HEART_MQTT_PREFIX+":"+devId+":cpu", Duration.ofHours(1));
            }
            if (StringUtils.isNotEmpty(memory)) {
                baseRedisListDataCache.init(REDIS_LIST_SIZE).addToList(HEART_MQTT_PREFIX + ":" + devId + ":memory", new RedisBaseDto(occurTime, memory));
                baseRedisListDataCache.setListExpiration(HEART_MQTT_PREFIX+":"+devId+":memory", Duration.ofHours(1));
            }
        }}catch (Exception e) {
            e.printStackTrace();
            System.err.println(e.getMessage());
        }
        System.err.println(heartBeatObj.toJSONString());
    }

    @PostMapping("/api/mqtt/log-upload")
    public void logUpload(@RequestBody(required = true) String message) {
        JSONObject uploadObj = JSONObject.parseObject(message);
        String devId = uploadObj.getString("devId");
        String timestamp = uploadObj.getString("timestamp");
        String fileName = uploadObj.getString("file_name");
        String path = uploadObj.getString("path");
        String type = uploadObj.getString("type");
        Integer level = uploadObj.getInteger("level");
        Integer uploadType = uploadObj.getInteger("upload_type");


        DmExceptionLogDto exceptionLogDto = new DmExceptionLogDto();
        exceptionLogDto.setType(type);
        exceptionLogDto.setLevel(level);
        exceptionLogDto.setDevId(devId);

        DmManDevicePo devicePo = manDeviceMapper.selectOne(Wrappers.<DmManDevicePo>lambdaQuery().eq(DmManDevicePo::getDeviceId, devId).last(SqlConstants.LIMIT_ONE));
        try {
            if (null!= devicePo) {
                exceptionLogDto.setManCode(devicePo.getManCode());
                exceptionLogDto.setTId(devicePo.getTId());

                exceptionLogDto.setOccurTime(DateUtils.dateToLocalDateTime(DateUtils.parseLongToDate(timestamp)));
                // 上传类型为崩溃上传时，插入异常日志
                if (uploadType == EmcsUploadType.getTypeCrackUpload()) {
                    exceptionLogMapper.insert(exceptionLogDto);
                }

                //DmExceptionLogDto 持久化
                DmDeviceLogFileDto deviceLogFileDto = new DmDeviceLogFileDto();
                deviceLogFileDto.setManDeviceId(devicePo.getId());
                deviceLogFileDto.setUrl(path);
                deviceLogFileDto.setType(uploadType);
                deviceLogFileDto.setName(fileName);

                deviceLogFileMapper.insert(deviceLogFileDto);

            } else {
                return;
            }
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }


    @GetMapping("/inner-api/devInfo/{devId}")
    public R<DmDigitalmanExtPo> devInnerInfo(@PathVariable(required = true) String devId) {

        if (StringUtils.isNotEmpty(devId)) {
           DmDigitalmanExtPo po  = dmDigitalmanService.selectExt(devId);
           return R.ok(po);
        }
        return R.fail("请检查请求参数");
    }


    @Autowired
    DmManDeviceServiceImpl dmManDeviceService;


    @GetMapping("/api/mansInfo")
    @Master
    public R<JSONObject> mansInfo() {

        List<DmManDeviceDto> dtos = dmManDeviceService.selectList(new DmManDeviceQuery());
        List<DmManDeviceDto> dtos2 = dtos.stream().filter(dto -> StringUtils.isNotEmpty(dto.getDeviceId())).collect(Collectors.toList());;
        Long serviceTimeCount = 0L;
        //当前时间和activateTime时间的差值，activateTime类型是Date，差值返回的单位是小时
        for (DmManDeviceDto dto : dtos2) {
            serviceTimeCount += (System.currentTimeMillis() - dto.getActivateTime().getTime())/3600000;
        }

        Integer meetingServiceCount = RedisUtil.getNumberVal("dashboard:meeting");
        Integer serverTimes = RedisUtil.getNumberVal("dashboard:server");
        Integer recognition = RedisUtil.getNumberVal("dashboard:recognition");
        Integer receptionCount = RedisUtil.getNumberVal("dashboard:create_visitor_info");
        Integer visitorCount = RedisUtil.getNumberVal("dashboard:register_visitor");
        Integer attendanceCount = RedisUtil.getNumberVal("dashboard:attendance");
        Integer openDoorCount = RedisUtil.getNumberVal("dashboard:open_door");
        Integer conversationDuration = RedisUtil.getNumberVal("dashboard:conversation-duration");
        Integer conversationTimes = RedisUtil.getNumberVal("dashboard:conversation-times");
        Integer broadcastCount = RedisUtil.getNumberVal("dashboard:broadcast-count");
        List<JSONObject> knowledgeList = new ArrayList<>();
        if (RedisUtil.existed("dashboard:knowledge-consume")) {
            knowledgeList = RedisUtil.getJsonList("dashboard:knowledge-consume");
            //取值后清除缓存list，5秒后过期
//            RedisUtil.expire("dashboard:knowledge-consume", 5);
        }
        List<JSONObject> skillList = new ArrayList<>();
        if (RedisUtil.existed("dashboard:skill-consume")) {
            skillList = RedisUtil.getJsonList("dashboard:skill-consume");
            //取值后清除缓存list，5秒后过期
//            RedisUtil.expire("dashboard:skill-consume", 5);
        }
        Long onlineCount = 0L;
        if (RedisUtil.existed(MessageConstants.REDIS_GROUP_DEVICE_HEADER)) {
            List<Object> manDeviceDtos = (List<Object>)redisTemplate.opsForHash().values(MessageConstants.REDIS_GROUP_DEVICE_HEADER);
            onlineCount = manDeviceDtos.stream().filter(item->(!((DmManDeviceDto)item).getOnlineStatus().isEmpty()) &&
                    ((DmManDeviceDto)item).getOnlineStatus().equalsIgnoreCase("1")).count();
            log.info("devs:{}, onlineCount:{}",manDeviceDtos.size(), onlineCount);
        }

        JSONArray jsonArray = new JSONArray();
//        最近三十天的数据，服务人次，知识库增量
        for (int i = 0; i < 30; i++) {
            Date date = DateUtils.addDays(new Date(), -i);
            String dateStr2 = DateUtils.formatDate(date, "yyyy-MM-dd");
            JSONObject json2 = new JSONObject();
            json2.put("date", dateStr2);
            json2.put("serviceTimes", RedisUtil.getNumberVal("dashboard:server-chart:"+dateStr2));
            json2.put("knowledgeNums", RedisUtil.getNumberVal("dashboard:server-chart:"+dateStr2));
            jsonArray.add(json2);
        }

        JSONObject json = new JSONObject();
        json.put("manCount",dtos2.size());//数字人数
        json.put("manOnlineCount",onlineCount);//数字人数
        json.put("recognizedPersonCount", recognition);//注册人员总数
        json.put("serviceTimeCount",serviceTimeCount);//总服务时间
        json.put("chatTimes",conversationTimes);//对话次数
        json.put("chatDurationCount",conversationDuration);//对话时长

        json.put("servicePerCount",serverTimes);//用户使用频次？改成服务人次
        json.put("serviceTotal",meetingServiceCount+receptionCount+visitorCount+attendanceCount+openDoorCount+broadcastCount);

        json.put("meetingServiceCount", meetingServiceCount);//会议
        json.put("receptionCount",receptionCount);//接待
        json.put("visitorCount",visitorCount);//访客
        json.put("attendanceCount",attendanceCount);//考勤
        json.put("openDoorCount",openDoorCount);//门禁
        json.put("broadcastCount",broadcastCount);//播报

        json.put("knowledgeConsume",knowledgeList);//实时知识库调用
        json.put("skillConsume",skillList);//实时技能调用
        json.put("serverCharts",jsonArray);//
        log.info(json.toJSONString());
        return R.ok(json);
    }


    @GetMapping("/api/devInfo/{devId}")
    public R<DmDigitalmanExtPo> devInfo(@PathVariable(required = true) String devId) {
        if (StringUtils.isNotEmpty(devId)){
            DeviceTenantSourceMergeVo vo = remoteDeviceTenantMergeService.selectDeviceTenantSourceMerge(devId);
            if (null!= vo){
                return remoteDigitalmanService.devInfo(devId, vo.getTenantId(), vo.getSourceSlave(), SecurityConstants.INNER);
            } else {
                return R.fail("devId可能不存在");
            }

        }
        return R.fail("请检查请求参数");
    }

    @GetMapping("/api/checkNetwork")
    public R netInfo() {
        return R.ok();
    }
}
