package com.xinda.gkb;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.xinda.common.annotation.SysProtocol;
import com.xinda.common.constant.XinDaConstant;
import com.xinda.common.core.mq.DeviceReport;
import com.xinda.common.core.mq.MQSendMessageBo;
import com.xinda.common.core.mq.message.DeviceData;
import com.xinda.common.core.mq.message.FunctionCallBackBo;
import com.xinda.common.core.redis.RedisCache;
import com.xinda.common.core.redis.RedisKeyBuilder;
import com.xinda.common.core.thingsModel.ThingsModelSimpleItem;
import com.xinda.common.exception.ServiceException;
import com.xinda.common.utils.DateUtils;
import com.xinda.common.utils.gateway.mq.TopicsUtils;
import com.xinda.iot.domain.Device;
import com.xinda.iot.model.ThingsModels.ThingsModelValueItem;
import com.xinda.iot.model.ThingsModels.ValueItem;
import com.xinda.iot.service.IDeviceService;
import com.xinda.protocol.base.protocol.IProtocol;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.math.BigDecimal;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author gsb
 * @date 2023/8/14 16:04
 */
@Slf4j
@Component
@SysProtocol(name = "IECF6113-JSON协议", protocolCode = XinDaConstant.PROTOCOL.IECF6113, description = "IECF6113-JSON协议")
public class IECF6113ProtocolService implements IProtocol {


    @Resource
    private RedisCache redisCache;
    @Resource
    private TopicsUtils topicsUtils;
    @Resource
    private IDeviceService deviceService;

    /**
     * 上报数据格式:              <p>
     * {
     * "AD6": 0.27333333333333,
     * "AD15": 0.27333333333333,
     * "AD14": 0.27333333333333,
     * "AD7": 0.27333333333333,
     * "AD11": 0.26,
     * "AD1": 0.27333333333333,
     * "AD4": 0.26,
     * "AD3": 0.26,
     * "AD10": 0.27333333333333,
     * "AD8": 0.26,
     * "AD16": 0.27333333333333,
     * "AD12": 0.26,
     * "AD13": 0.27333333333333,
     * "AD9": 0.27333333333333,
     * "AD2": 0.27333333333333,
     * "AD5": 0.27333333333333
     * }
     * <p>
     * {
     * "I0.1": 0,
     * "I0.3": 0,
     * "I0.4": 0,
     * "I0.6": 0,
     * "I0.5": 0,
     * "I0.2": 0
     * }
     * <p>
     * {
     * "lng": "104.0902886",
     * "lat": "030.8176553"
     * }
     * <p>
     * {
     * "PAD": 3905
     * }
     * <p>
     * <p>
     * <p>
     * 下发报文格式<p>
     * device   从机编号  <p>
     * name     标识符    <p>
     * value    值        <p>
     * serNo    流水号    <p>
     * {
     * "device": 1,
     * "name": "template",
     * "value": 111,
     * "serNo": "213245489543789"
     * }
     * </p>
     * <p>
     * 下发指令回复格式<p>
     * serNo   平台的流水号，用于对应回复消息   <p>
     * ack     下发指令状态 0是失败 1是成功    <p>
     * {
     * "serNo": "213245489543789",
     * "ack": 1
     * }
     * </p>
     * <p>
     * {"AD6":0.20666666666667,"AD15":0.22,"AD14":0.22,"AD7":0.20666666666667,"AD11":0.20666666666667,
     * "AD1":18.886666666667,"AD4":0.22,"AD3":0.22,"AD10":0.22,"AD8":15.526666666667,"AD16":0.22,"AD12":0.22,
     * "AD13":0.22,"AD9":0.20666666666667,"AD2":0.22,"AD5":0.22}
     * {"I0.1":0,"I0.3":0,"I0.4":0,"I0.6":0,"I0.5":0,"I0.2":0}
     * {"K0":1001,"温度":17.9375,"K1":48303.94921875,"标况流量":0,"工况流量":0,"压力":96.8515625}
     * {"lng":"104.1536100","lat":"030.8336398","PAD":4185,"CSQ":28}
     */
    private static final Set<String> EXCLUDE_KEYS = new HashSet<>(Arrays.asList(
            "AD1", "AD2", "AD3", "AD4", "AD5", "AD6", "AD7", "AD8", "AD9", "AD10", "AD11", "AD12",
            "AD13", "AD14", "AD15", "AD16", "I0.1", "I0.2", "I0.3", "I0.4", "I0.5", "I0.6", "lng", "lat", "PAD", "CSQ",
            "K0", "K1", "温度", "温度1", "标况流量", "工况流量", "压力"
    ));

    @Override
    public DeviceReport decode(DeviceData deviceData, String clientId) {
        try {
            DeviceReport reportMessage = new DeviceReport();
            String data = new String(deviceData.getData(), StandardCharsets.UTF_8);
            List<ThingsModelSimpleItem> result = new ArrayList<>();
            if (data.trim().equals("ok") || data.trim().equals("ERROR")) {
                log.info(data);
            } else {
                Map<String, Object> values = JSON.parseObject(data, Map.class);
//                if (values.containsKey("AD1") || values.containsKey("lng") || values.containsKey("I0.1")||values.containsKey("PAD")||values.containsKey("K1")){
                // 初始化纬度变量，使用BigDecimal来存储高精度值
                BigDecimal latitude = new BigDecimal("39.9042");
                // 初始化经度变量，同样使用BigDecimal
                BigDecimal longitude = new BigDecimal("116.4074");
                String volt = "";
                Integer rssi = 0;
                String sd = "";
                String pd = "";
                String imei = "";
                String iccid = "";
                Device device = new Device();
                for (Map.Entry<String, Object> entry : values.entrySet()) {
//                        if (EXCLUDE_KEYS.contains(entry.getKey())){
                    ThingsModelSimpleItem simpleItem = new ThingsModelSimpleItem();
                    simpleItem.setTs(DateUtils.getNowDate());
                    simpleItem.setId(entry.getKey());
                    simpleItem.setValue(entry.getValue() + "");
                    result.add(simpleItem);
                    if (entry.getKey().equals("lng")) {
                        longitude = new BigDecimal(entry.getValue() + "");
                        device.setLongitude(longitude);

                    }
                    if (entry.getKey().equals("lat")) {
                        latitude = new BigDecimal(entry.getValue() + "");
                        device.setLatitude(latitude);

                    }
                    if (entry.getKey().equals("PAD")) {
//                        volt = entry.getValue() + "";
                        BigDecimal  num = new BigDecimal( entry.getValue() + "");
                        volt = num.divide(new BigDecimal("1000"), 2, BigDecimal.ROUND_HALF_UP).toPlainString();
                        device.setVolt(volt);
                    }
                    if (entry.getKey().equals("CSQ")) {
                        rssi = Integer.parseInt(entry.getValue() + "");
                        device.setRssi(rssi);
                    }

                    if (entry.getKey().equals("SD")) {
                        BigDecimal  num1 = new BigDecimal( entry.getValue() + "");
                        sd = num1.divide(new BigDecimal("1000"), 3, BigDecimal.ROUND_HALF_UP).toPlainString();
                        device.setSd(sd);

                    }
                    if (entry.getKey().equals("BD")) {
                        BigDecimal  num2 = new BigDecimal( entry.getValue() + "");
                        pd = num2.divide(new BigDecimal("1000"), 3, BigDecimal.ROUND_HALF_UP).toPlainString();
                        device.setPd(pd);
                    }
                    if (entry.getKey().equals("ICCID")) {
                        iccid = entry.getValue() + "";
                        device.setIccid(iccid);
                    }
                    if (entry.getKey().equals("IMEI")) {
                        imei = entry.getValue() + "";
                        device.setImei(imei);
                    }
//                        }
                }
                if (values.containsKey("lng")) {
                    //更新设备经纬度信息
//                    Device device = new Device();
                    device.setSerialNumber(deviceData.getSerialNumber());
//                    device.setLongitude(longitude);
//                    device.setLatitude(latitude);
//                    device.setVolt(volt);
//                    device.setRssi(rssi);
//                    device.setSd(sd);
//                    device.setPd(pd);
                    deviceService.updateDeviceStatus(device);
                }


            }
            reportMessage.setThingsModelSimpleItem(result);
            reportMessage.setClientId(clientId);
            reportMessage.setSerialNumber(clientId);
            return reportMessage;
        } catch (Exception e) {
            throw new ServiceException("数据解析异常" + e.getMessage());
        }
    }

    /**
     * 1.查询模拟量 GKB29810,ADC,8
     * 2.查询 485 上报 GKB29810,command,1
     * 3.查询输入开关量 GKB29810,IPUT,
     * 4.更改上报时间 GKB29810,JCZ,{"TIME":20}
     * 5.通 信 控制 GKB29810,MODBUS, {"KO":12}
     * 6.开关量输出控制 GKB29810,OUT,{"Q0.1":0,"Q0.2":0,"Q0.3":0,"Q0.4":0,"Q0.5":0,"Q0.6":0}
     * GKB29810,0UT,{"Q0.1":1 ,"Q0.2":1,"Q0.3":1,"Q0.4":1,"Q0.5":1 "Q0.6":1)
     *
     * @param message
     * @return
     */
    @Override
    public FunctionCallBackBo encode(MQSendMessageBo message) {
        try {
            FunctionCallBackBo callBack = new FunctionCallBackBo();
            JSONObject params = message.getParams();
//            Map<String,Object> values = JSON.parseObject(data, Map.class);
//            JSONObject subJson = new JSONObject();
//            for (Map.Entry<String, Object> entry : params.entrySet()) {
//                String key = entry.getKey();
//                if (checkVariable(key)) {
//                       subJson.put(key, params.get(key));
//                    }
//            }
//            if (!subJson.isEmpty()) {
//                String msg = "GKB29810,MODBUS, " + subJson.toJSONString();
//                callBack.setSources(msg);
//                callBack.setMessage(msg.getBytes());
//            }
            // 调用方法检查并获取以前缀开头的键和值
            Map<String, Object> modresult = getValidKeyValuesToMap(params);
            // 输出结果
            if (!modresult.isEmpty()) {
                StringBuilder msg = new StringBuilder("GKB29810,MODBUS,{\"");
                modresult.forEach((k, v) -> {
                    msg.append(k).append("\":").append(v).append(",");
                });
                // 删除最后一个逗号
                if (msg.charAt(msg.length() - 1) == ',') {
                    msg.deleteCharAt(msg.length() - 1);
                }
                // 添加结尾的双引号和大括号
                msg.append("}");
                callBack.setSources(msg.toString());
                callBack.setMessage(msg.toString().getBytes());
            }

            // 调用方法检查并获取以前缀开头的键和值
            Map<String, Object> chinaresult = getChineseKeyValues(params);
            // 输出结果
            if (!chinaresult.isEmpty()) {
                StringBuilder msg = new StringBuilder("GKB29810,MODBUS,{\"");
                chinaresult.forEach((k, v) -> {
                    msg.append(k).append("\":").append(v).append(",");
                });
                // 删除最后一个逗号
                if (msg.charAt(msg.length() - 1) == ',') {
                    msg.deleteCharAt(msg.length() - 1);
                }
                // 添加结尾的双引号和大括号
                msg.append("}");
                callBack.setSources(msg.toString());
                callBack.setMessage(msg.toString().getBytes());
            }

            if (params.containsKey("select")) {
                String msg = params.get("select") + "";
                callBack.setSources(msg);
                callBack.setMessage(msg.getBytes());
            }
            if (params.containsKey("OUT")) {
                String msg = params.get("OUT") + "";
                callBack.setSources(msg);
                callBack.setMessage(msg.getBytes());
            }
            if (params.containsKey("TIME")) {
                String msg = "GKB29810,JCZ,{\"TIME\":" + params.get("TIME") + "}";
                callBack.setSources(msg);
                callBack.setMessage(msg.getBytes());
            }
            // 调用方法检查并获取以前缀开头的键和值
            Map<String, Object> result = getKeysAndValuesWithPrefix(params, "Q0.");
            // 输出结果
            if (!result.isEmpty()) {
                StringBuilder msg = new StringBuilder("GKB29810,OUT,{\"");
                result.forEach((k, v) -> {
                    msg.append(k).append("\":").append(v).append(",");
                });
                // 删除最后一个逗号
                if (msg.charAt(msg.length() - 1) == ',') {
                    msg.deleteCharAt(msg.length() - 1);
                }
                // 添加结尾的双引号和大括号
                msg.append("}");
                callBack.setSources(msg.toString());
                callBack.setMessage(msg.toString().getBytes());
            }
            return callBack;
        } catch (Exception e) {
            log.error("=>指令编码异常,device={},data={}", message.getSerialNumber(),
                    message.getParams());
            return null;
        }
    }


    /**
     * 获取JSONObject中以指定前缀开头的键和值
     *
     * @param jsonObject 要检查的JSONObject
     * @param prefix     要匹配的前缀
     * @return 一个包含匹配键和值的Map，如果没有匹配的键，则返回一个空的Map
     */
    public static Map<String, Object> getKeysAndValuesWithPrefix(JSONObject jsonObject, String prefix) {
        Map<String, Object> result = new HashMap<>();

        // 遍历JSONObject的所有键值对
        for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
            String key = entry.getKey();
            // 检查当前键是否以指定前缀开头
            if (key.startsWith(prefix)) {
                // 如果匹配，将键和值添加到结果Map中
                result.put(key, entry.getValue());
            }
        }

        return result;
    }


    // 生成所有符合规则的变量集合
    private static Set<String> generateAllValidVariables() {
        Set<String> validVariables = new HashSet<>();
        // 生成字节型变量
        for (int i = 0; i <= 30000; i++) {
            validVariables.add("MB" + i);
            validVariables.add("VB" + i);
        }
        // 生成字型变量
        for (int i = 0; i <= 30000; i++) {
            validVariables.add("MW" + i);
            validVariables.add("VW" + i);
        }
        // 生成双字型变量
        for (int i = 0; i <= 30000; i++) {
            validVariables.add("MD" + i);
            validVariables.add("VD" + i);
        }
        // 生成其他变量
        for (int i = 0; i <= 30000; i++) {
            validVariables.add("M" + i);
            validVariables.add("D" + i);
            validVariables.add("Y" + i);
            validVariables.add("N" + i);
            validVariables.add("Q" + i);
        }
        return validVariables;
    }

    // 获取包含在指定变量范围内的 params 变量的键和值
    public static JSONObject getValidKeyValues(JSONObject params) {
        Set<String> validVariables = generateAllValidVariables();
        JSONObject validKeyValues = new JSONObject();
        for (String key : params.keySet()) {
            if (validVariables.contains(key)) {
                validKeyValues.put(key, params.get(key));
            }
        }
        return validKeyValues;
    }

    // 检测 params 中的键是否在有效变量范围内
    public static boolean checkParams(JSONObject params) {
        Set<String> validVariables = generateAllValidVariables();
        for (String key : params.keySet()) {
            if (validVariables.contains(key)) {
                return true;
            }
        }
        return false;
    }

    // 获取包含在指定变量范围内的 params 变量的键和值，并存储到 Map 中
    public static Map<String, Object> getValidKeyValuesToMap(JSONObject params) {
        Set<String> validVariables = generateAllValidVariables();
        Map<String, Object> result = new HashMap<>();
        for (String key : params.keySet()) {
            if (validVariables.contains(key)) {
                result.put(key, params.get(key));
            }
        }
        return result;
    }


    // 检查字符串是否包含中文
    private static boolean containsChinese(String str) {
        String regex = ".*[\u4e00-\u9fa5].*";
        return Pattern.matches(regex, str);
    }

    // 获取 params 里名字包含中文的变量的键和值
    public static Map<String, Object> getChineseKeyValues(JSONObject params) {
        Map<String, Object> result = new HashMap<>();
        for (String key : params.keySet()) {
            if (containsChinese(key)) {
                result.put(key, params.get(key));
            }
        }
        return result;
    }


}
