package com.artfess.device.base.manager.impl;

import com.artfess.device.base.utils.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.artfess.base.constants.CacheKeyConst;
import com.artfess.base.exception.ApplicationException;
import com.artfess.base.manager.impl.BaseManagerImpl;
import com.artfess.base.util.DateUtils;
import com.artfess.base.util.JsonUtil;
import com.artfess.base.util.StringUtil;
import com.artfess.device.base.config.MqConstant;
import com.artfess.device.base.dao.DeviceStatusLogDao;
import com.artfess.device.base.dao.DeviceVideoPointDao;
import com.artfess.device.base.dao.DeviceVideoPointExtendDao;
import com.artfess.device.base.dao.DeviceVideoZoneDao;
import com.artfess.device.base.manager.DeviceVideoPointManager;
import com.artfess.device.base.manager.DeviceVideoZoneManager;
import com.artfess.device.base.manager.DeviceWarnInfoManager;
import com.artfess.device.base.model.DeviceStatusLog;
import com.artfess.device.base.model.DeviceVideoPoint;
import com.artfess.device.base.model.DeviceVideoPointExtend;
import com.artfess.device.base.model.DeviceVideoZone;
import com.artfess.device.base.model.DeviceWarnInfo;
import com.artfess.device.base.vo.DeviceWarnInfoVo;
import com.artfess.redis.util.RedisUtil;
import com.artfess.rocketmq.producer.RocketMQProducer;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 视频点位信息表（DEVICE_VIDEO_POINT） 服务实现类
 *
 * @author min.wu
 * @company 阿特菲斯信息技术有限公司
 * @since 2022-08-19
 */
@Service
public class DeviceVideoPointManagerImpl extends BaseManagerImpl<DeviceVideoPointDao, DeviceVideoPoint> implements DeviceVideoPointManager {
    @Autowired
    private DeviceVideoZoneManager deviceVideoZoneManager;

    @Resource
    private DeviceVideoPointExtendDao videoPointExtendDao;

    @Resource
    DeviceStatusLogDao statusLogDao;

    @Resource
    private DeviceVideoZoneDao deviceVideoZoneDao;

    @Autowired
    private DeviceWarnInfoManager warnInfoManager;

    @Autowired
    private RocketMQProducer rocketMQUtils;

    @Autowired
    private RedisUtil redisUtil;

    @Resource
    private RestTemplate restTemplate;

    final String  ZGrootUri="http://92.94.92.16:11125";  //华智根路径

    @Override
    public void deleteZoneRes(String zoneCode) {
        this.baseMapper.deleteZoneRes(zoneCode);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String updateInfo(DeviceVideoPointExtend t) {
        QueryWrapper<DeviceVideoPointExtend> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("code_", t.getCode());
        videoPointExtendDao.delete(queryWrapper);
        videoPointExtendDao.insert(t);
        return t.getId();
    }

    @Override
    public DeviceVideoPoint findByCode(String code) {
        QueryWrapper<DeviceVideoPoint> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("code_", code);
        DeviceVideoPoint deviceVideoPoint = this.baseMapper.selectOne(queryWrapper);
        if (null == deviceVideoPoint) {
            return new DeviceVideoPoint();
        }
        QueryWrapper<DeviceVideoPointExtend> query = new QueryWrapper<>();
        query.eq("code_", code);
        DeviceVideoPointExtend deviceVideoPointExtend = videoPointExtendDao.selectOne(query);
        deviceVideoPoint.setDeviceVideoPointExtend(deviceVideoPointExtend);

        return deviceVideoPoint;
    }

    @Override
    @Transactional
    public Boolean updateVideoStatus(String deviceCode, String status) {
        Assert.hasText(deviceCode, "设备标识不能为空！");
        Assert.hasText(status, "设备状态不能为空！");
        Integer result = null;
        //先查询日志表中是否有日志数据
        QueryWrapper<DeviceStatusLog> statusWrapper = new QueryWrapper<>();
        statusWrapper.eq("device_code_", deviceCode);
        statusWrapper.eq("end_time_", "9999-12-31 23:59:59");
        DeviceStatusLog deviceStatusLog = this.statusLogDao.selectOne(statusWrapper);
        if (deviceStatusLog !=null) { //有日志数据就判断状态是否改变
            LocalDateTime times = LocalDateTime.now();
            //先修改最后一条状态日志的结束时间
            if (!deviceStatusLog.getDeviceStatus().equals(status)) {
                Duration duration = Duration.between(deviceStatusLog.getStartTime(), times);
                deviceStatusLog.setTimeLength(duration.toMinutes());
                deviceStatusLog.setEndTime(times);
                deviceStatusLog.setLastTime(LocalDateTime.now());
                this.statusLogDao.updateById(deviceStatusLog);

                //新建一条当前状态日志数据
                DeviceStatusLog newDeviceStatusLog = new DeviceStatusLog();
                newDeviceStatusLog.setDeviceCode(deviceCode);
                newDeviceStatusLog.setDeviceStatus(status);
                newDeviceStatusLog.setDeviceType("5");
                newDeviceStatusLog.setStartTime(times);
                newDeviceStatusLog.setEndTime(LocalDateTime.of(9999, 12, 31, 23, 59, 59));
                result = this.statusLogDao.insert(newDeviceStatusLog);
            }
            //发送设备的状态到MQ
            Map<String, Object> deviceStatus = new HashMap<>();
            deviceStatus.put("deviceCode", deviceCode);
            deviceStatus.put("proType", "5");
            deviceStatus.put("status", status);
            String deviceStatusString = JSON.toJSONString(deviceStatus);
            try {
                rocketMQUtils.send(MqConstant.SEND_STATUS_TOPIC, deviceStatusString);
            } catch (Exception e) {
                log.error("设备状态发送失败:{}", e);
            }

        } else {
            //没有日志数据就添加一条
            DeviceStatusLog newDeviceStatusLog = new DeviceStatusLog();
            newDeviceStatusLog.setDeviceCode(deviceCode);
            newDeviceStatusLog.setDeviceStatus(status);
            newDeviceStatusLog.setDeviceType("5");
            newDeviceStatusLog.setStartTime(LocalDateTime.now());
            newDeviceStatusLog.setEndTime(LocalDateTime.of(9999, 12, 31, 23, 59, 59));
            result = this.statusLogDao.insert(newDeviceStatusLog);
            //发送设备的状态到MQ
            Map<String, Object> deviceStatus = new HashMap<>();
            deviceStatus.put("deviceCode", deviceCode);
            deviceStatus.put("proType", "5");
            deviceStatus.put("status", status);
            String deviceStatusString = JSON.toJSONString(deviceStatus);
            try {
                rocketMQUtils.send(MqConstant.SEND_STATUS_TOPIC, deviceStatusString);
            } catch (Exception e) {
                log.error("设备状态发送失败:{}", e);
            }
        }

        QueryWrapper<DeviceVideoPoint> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("code_", deviceCode);
        DeviceVideoPoint videoPoint = this.getOne(queryWrapper);
        //状态改变了就要修改状态和修改最后一条日志的结束时间和添加新状态的日志
        if (videoPoint != null && !videoPoint.getStatus().equals(status)) {
            videoPoint.setStatus(status);
            videoPoint.setLastTime(LocalDateTime.now());
            this.updateById(videoPoint);
        }
        return null != result && result >= 1;
    }

    @Override
    public String getZGVideoUri(String code,Integer module,boolean refresh) {
        Assert.hasText(code, "视频点位编码不能为空！");
        QueryWrapper<DeviceVideoPoint> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("code_", code);
        List<DeviceVideoPoint> list = this.baseMapper.selectList(queryWrapper);
        if(list == null || list.size()==0){
            throw  new ApplicationException("没有查询到编码为【"+code+"】的摄像头！");
        }
        DeviceVideoPoint deviceVideoPoint = list.get(0);
        if(!deviceVideoPoint.getPlatform().equals("ZG")){
            throw  new ApplicationException("摄像头【"+code+"】不是紫光华智的摄像头！");
        }
        String token = this.getZGToken(false);
        Map<String,Object> param =new HashMap<>();
        if(module == null || module > 8 || module < 1){
            module = 5;
        }
        param.put("channel_code",code);
        param.put("stream_mode",module);
        param.put("stream_type",0);
        param.put("keep_alive",600);
        Map<String,String> head =new HashMap<>();
        head.put("Authorization",token);
        try {
            String uri = ZGrootUri+"/api/vms/v2/webuas/live/stream/url";
            //log.info("请求地址: {},请求参数: {}",uri,JSON.toJSONString(param));
            String result = HttpUtil.get(uri,param,head,null,0,0,null);
            //log.info("响应: {}",result);
            if(result == null){
                throw new ApplicationException("获取海康监控点失败!");
            }
            JsonNode root = JsonUtil.toJsonNode(result);
            //token过期
            if(root.has("error_code")&& root.get("error_code").asText().indexOf("expired_accessToken")!=-1){
                getZGVideoUri(code,module,true);
            }

            if(root.has("data") ){
                return root.get("data").asText();
            }else{
                throw new ApplicationException("没有查询到视频流播放地址！");
            }
        } catch (Exception e) {
            log.error(e.getMessage(),e);
        }
        return "";
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer asycZGVideoPoint(boolean refresh) {
        QueryWrapper<DeviceVideoPoint> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("platform_", "ZG");
        this.baseMapper.delete(queryWrapper);

        QueryWrapper<DeviceVideoZone> zoneWrapper = new QueryWrapper<>();
        zoneWrapper.eq("platform_", "ZG");
        this.deviceVideoZoneDao.delete(zoneWrapper);

        this.asycZGZone(refresh);

        List<DeviceVideoPoint> pointList = this.asycZGcameras(refresh);
        this.saveBatch(pointList);
        return pointList.size();
    }

    /**
     * 同步华智监控点资源
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public void asycZGZone(boolean refresh){
        String token = this.getZGToken(refresh);
        Map<String,Object> param =new HashMap<>();
        param.put("org_index","500199");
        Map<String,String> head =new HashMap<>();
        head.put("Authorization",token);
        List<DeviceVideoPoint> list = Lists.newArrayList();
        try {
            String uri = ZGrootUri+"/api/bss/v1/uuv/users/privilege/device-org/tree";
            //log.info("请求地址: {},请求参数: {}",uri,JSON.toJSONString(param));
            String result = HttpUtil.get(uri,param,head,null,0,0,null);
            //log.info("响应: {}",result);
            if(result == null){
                throw new ApplicationException("获取紫光华智监控点失败!");
            }
            JsonNode root = JsonUtil.toJsonNode(result);
            //token过期
            if(root.has("error_code")&& root.get("error_code").asText().indexOf("expired_accessToken")!=-1){
                asycZGZone(true);
            }

            if(root.has("data")&&root.get("data")!=null &&!root.get("data").asText().equals("null")){
                // log.info("===========data: {}",root.get("data"));
                saveZGZone(root.get("data"));
                // log.info("==========保存完成！=============");
            }
        } catch (Exception e) {
            log.error(e.getMessage(),e);
        }
    }

    private void saveZGZone(JsonNode data){
        if(data!=null){
            ArrayNode dataList = (ArrayNode)data;
            List<JsonNode> childNodes = Lists.newArrayList();
            for(JsonNode vnode : dataList){
                DeviceVideoZone res = new DeviceVideoZone();
                res.setBaseCode("zg001");
                res.setPlatform("ZG");
                res.setCode(vnode.get("org_index").asText());
                res.setName(vnode.get("org_name").asText());
                if(vnode.get("org_parent_index").asText().equals("5001")){
                    res.setParentId("-1");
                }else{
                    res.setParentId(vnode.get("org_parent_index").asText());
                }
                res.setId(vnode.get("org_index").asText());
                res.setCreateTime(LocalDateTime.now());
                res.setUpdateTime(LocalDateTime.now());
                res.setLastTime(LocalDateTime.now());
                //   log.info("保存数据: {}",res.toString());
                deviceVideoZoneManager.saveTreeAndId(res);
                //   log.info("=========child: {}",vnode.get("child"));
                if(vnode.has("child") && vnode.get("child")!=null && !vnode.get("child").asText().equals("null")){
                    childNodes.add(vnode.get("child"));
                }

            }
            //  log.info("=========child个数: {}",childNodes.size());
            if(childNodes!=null && childNodes.size()>0){
                for (JsonNode childNode : childNodes){
                    saveZGZone(childNode);
                }
            }
        }

    }

    @Transactional(rollbackFor = Exception.class)
    public  List<DeviceVideoPoint> asycZGcameras(boolean refresh){
        String token = this.getZGToken(refresh);
        Map<String,Object> param =new HashMap<>();
        param.put("sub_type",1);
        param.put("resource_type",6);
        param.put("order_field","ape_id");
        param.put("order_rule","asc");
        param.put("page_num",1);
        param.put("page_size",26000);
        param.put("org_index",500199);
        Map<String,String> head =new HashMap<>();
        head.put("Authorization",token);
        List<DeviceVideoPoint> list = Lists.newArrayList();
        try {
            String result = HttpUtil.get(ZGrootUri+"/api/bss/v1/udm/channel/list-with-device",param,head,null,0,0,null);
            //log.info("==============result:{}",result);
            if(result == null){
                throw new ApplicationException("获取海康监控点失败!");
            }
            JsonNode root = JsonUtil.toJsonNode(result);

            //token过期
            if(root.has("error_code")&& root.get("error_code").asText().indexOf("expired_accessToken")!=-1){
                asycZGcameras(true);
            }
            if(root.has("data")&&root.get("data")!=null){
                if(root.get("data").has("data")){
                    List<DeviceWarnInfo> warnInfoList = new ArrayList<>();
                    ArrayNode dataList = (ArrayNode)root.get("data").get("data");
                    //log.info("==============dataList:{}",dataList);
                    for(JsonNode vnode : dataList){
                        //    log.info("==============vnode:{}",vnode);
                        DeviceVideoPoint res = new DeviceVideoPoint();
                        res.setCatalogCode("zg001");
                        res.setZoneCode(vnode.get("org_index").asText());
                        res.setCode(vnode.get("ape_id").asText());
                        res.setPlatform("ZG");
                        res.setAisle(vnode.get("idx").asText());
                        res.setName(vnode.get("name").asText());
                        if(vnode.get("longitude")!=null){
                            res.setLgtd(vnode.get("longitude").asText());
                        }
                        if(vnode.get("latitude")!=null){
                            res.setLttd(vnode.get("latitude").asText());
                        }
                        if(vnode.get("ip_addr")!=null){
                            res.setIp(vnode.get("ip_addr").asText());
                        }
                        res.setAddress(vnode.get("name").asText());
                        if(vnode.get("is_online").asText().equals("1")){
                            res.setStatus("1");
                        }else{
                            res.setStatus("0");
                        }
                        res.setId(vnode.get("ape_id").asText());
                        res.setCreateTime(LocalDateTime.now());
                        res.setLastTime(LocalDateTime.now());
                        res.setUpdateTime(LocalDateTime.now());
                        list.add(res);

                        //判断不在线情况
                        //  log.info("判断不在线情况!");
                        String code = vnode.get("ape_id").asText();
                        if(vnode.get("is_online")==null || !vnode.get("is_online").asText().equals("1")){
                            updateVideoStatus(code,"2");
                            //获取该设备的最新一条报警数据，判断其有没有处理，如果没有处理就不增加报警数据，如果处理了就新增报警数据
                            DeviceWarnInfo warnInfo = warnInfoManager.getNewWarnInfo(code,"1");
                            if(warnInfo == null ||(warnInfo!=null && warnInfo.getHandleStatus()==1)){
                                warnInfo = new DeviceWarnInfo();
                                warnInfo.setDeviceCode(code);
                                warnInfo.setWarnName("视频设备【"+vnode.get("name").asText()+"】离线告警！");
                                warnInfo.setWarnType("1");
                                warnInfo.setDeviceType("5");
                                warnInfo.setWarnReason("视频设备【"+vnode.get("name").asText()+"】离线，可能发生故障！");
                                warnInfo.setWarnTime(res.getCreateTime());
                                warnInfo.setCreateTime(LocalDateTime.now());
                                warnInfo.setHandleStatus(0);
                                warnInfoList.add(warnInfo);

                                //上报事件中心
                                DeviceWarnInfoVo deviceWarnInfoVo = new DeviceWarnInfoVo();
                                deviceWarnInfoVo.setDevName(vnode.get("name").asText());
                                deviceWarnInfoVo.setDevCode(warnInfo.getDeviceCode());
                                deviceWarnInfoVo.setEventSrcId(warnInfo.getId());
                                deviceWarnInfoVo.setEventTitle(warnInfo.getWarnName());
                                deviceWarnInfoVo.setEventTime(DateUtils.now());
                                deviceWarnInfoVo.setEventSubClass(warnInfo.getDeviceType());
                                deviceWarnInfoVo.setEventDesc(warnInfo.getWarnReason());
                                deviceWarnInfoVo.setEventType(Integer.valueOf(warnInfo.getWarnType()));
                                deviceWarnInfoVo.setEventAddr(vnode.get("name").asText());
                                if(vnode.get("latitude")!=null){
                                    deviceWarnInfoVo.setEventLat(new BigDecimal(vnode.get("latitude").asDouble()));
                                }
                                if(vnode.get("longitude")!=null){
                                    deviceWarnInfoVo.setEventLng(new BigDecimal(vnode.get("longitude").asDouble()));
                                }

                                DeviceVideoPointExtend videoPointExtend = videoPointExtendDao.findVideoExtendByCode(warnInfo.getDeviceCode());
                                if(videoPointExtend!=null){
                                    deviceWarnInfoVo.setRegionCode(videoPointExtend.getAreaCode());
                                    deviceWarnInfoVo.setRegionName(videoPointExtend.getAreaName());
                                }
                                //上传报警给MQ
                                String warnInfoString = JSON.toJSONString(deviceWarnInfoVo);
                                try{
                                    rocketMQUtils.send(MqConstant.SEND_WARNING_TOPIC, warnInfoString);
                                }catch (Exception e) {
                                    log.error("发送失败:{}", e);
                                }
                            }
                        }
                        if(vnode.get("is_online").asText().equals("1")){
                            updateVideoStatus(code,"1");
                        }
                    }
                }else{
                    log.error("没有查询到数据！");
                }
            }else{
                log.error("没有返回数据！");
            }
        } catch (Exception e) {
            log.error(e.getMessage(),e);
        }
        return list;
    }

    public  String getZGToken(boolean refresh){
        String token = "";
        if(!refresh){
            token = redisUtil.get(CacheKeyConst.BIZ_ZG_TOKEN, String.class);
        }

        if(StringUtil.isEmpty(token)){
            HttpHeaders headers = new HttpHeaders();
            HttpMethod method = HttpMethod.POST;
            // 以表单的方式提交
            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
            // 将请求头部和参数合成一个请求
            HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(null, headers);
            String url = "http://92.94.92.16:11125/sso/oauth2.0/accessToken?grant_type=client_credentials&client_id=user003&client_secret=user003&format=json";
            // log.info("请求地址{}", url);
            try {
                //用HttpEntity封装整个请求报文
                ResponseEntity<String> result = restTemplate.exchange(url, method,requestEntity, String.class);
                //     log.info("响应body: {}", JSON.toJSONString(result.getBody()));
                JsonNode root = JsonUtil.toJsonNode(result.getBody());
                token =  root.get("access_token").asText();
                Integer times = root.get("expires_in").asInt();
                if(times ==null || times ==0){
                    times = 6000;
                }
                redisUtil.set(CacheKeyConst.BIZ_ZG_TOKEN, token, times);
            } catch (Exception e) {
                log.error(e.getMessage(),e);
            }
        }
        return token;
    }

}
