package com.xhpc.pp.server;

import cn.hutool.core.date.DateUtil;
import com.alibaba.nacos.api.exception.NacosException;
import com.xhpc.common.api.dto.ChargingStationDto;
import com.xhpc.common.enums.StationDeviceEnum;
import com.xhpc.pp.domain.ServiceField;
import com.xhpc.pp.domain.XhpcDeviceMessage;
import com.xhpc.pp.logic.FieldLogic;
import com.xhpc.pp.logic.ServiceMainLogic;
import com.xhpc.pp.mapper.XhpcDeviceMessageMapper;
import com.xhpc.pp.tx.ServiceParameter;
import com.xhpc.pp.tx.ServiceResult;
import com.xhpc.pp.tx.TxException;
import com.xhpc.pp.utils.HexUtils;
import com.xhpc.pp.utils.SpringContextHolder;
import com.xhpc.pp.utils.security.CRCCalculator;
import org.apache.commons.lang3.ArrayUtils;
import org.quickserver.net.server.ClientBinaryHandler;
import org.quickserver.net.server.ClientHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMAT;
import static com.xhpc.common.data.redis.StaticBeanUtil.REDIS;
import static com.xhpc.common.data.redis.StaticBeanUtil.seqHex;
import static com.xhpc.pp.server.ChargingPileServer.removeHandler;
import static com.xhpc.pp.tx.ServiceResult.OK;
import static com.xhpc.pp.utils.HexUtils.toHex;
import static com.xhpc.pp.utils.security.CP56Time2a.toCp56Hex;
import static com.xhpc.pp.utils.security.CRCCalculator.calcCrc;
import static com.xhpc.pp.utils.security.CacheDataUtils.hori2;

public class ChargingPileBinaryHandler implements ClientBinaryHandler {

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

    private static final String SERVICE_REGISTER = "01";
    private static final String SERVICE_HB = "03";
    private static final String SERVICE_RMR = "09";
    private static final String SERVICE_OD = "3B";
    private static final String SERVICE_RMCR = "57";

    private static final String DATA_TYPE_STRING = "string";
    private static final String DATA_TYPE_INT = "int";
    private static final String DATA_TYPE_LONG = "long";
    // private static final String DATA_TYPE_HEX = "hex";

    private final ServiceMainLogic servicemainLogic;
    private final XhpcDeviceMessageMapper xhpcDeviceMessageMapper;


    public ChargingPileBinaryHandler() {

        servicemainLogic = SpringContextHolder.getBean("ServiceMainLogic");
        xhpcDeviceMessageMapper = SpringContextHolder.getBean(XhpcDeviceMessageMapper.class);
    }

    @Override
    public void handleBinary(ClientHandler handler, byte[] data) throws IOException {

        try {
            String pileNo = ChargingPileServer.getPileNo(handler);
            List<byte[]> dataList = parseDataList(data);
            for (byte[] d : dataList) {
                String dataStr = toHex(d);
                log.debug("received data <<<< |{}| from pile <- ({})", dataStr, pileNo);
                if (d.length <= 2 || !dataStr.startsWith("68")) {
                    log.debug("received invalid data <<<< |{}|, len[{}]", dataStr, d.length);
                    continue;
                }
                int len = HexUtils.toInteger(d, 1, 2);
                if (d.length < len) {
                    log.error("incorrect input data |{}| len[{}]", dataStr, d.length);
                }
                String crc = calcCrc(d);
                //log.info("====111=====crc==================="+crc+"==========dataStr==============="+dataStr);
                if (!dataStr.endsWith(crc)) {
                    log.error("incorrect input data crc {}", crc);
                    continue;
                }
                process(handler, d);
            }
        } catch (TxException | NacosException | InterruptedException e) {
            log.error(e.getMessage(), e);
        }
    }

    private void process(ClientHandler handler, byte[] data) throws TxException, IOException, NacosException,
            InterruptedException {

        String serviceName = toHex(data, 5, 6);
        //log.error("==========ChargingPileBinaryHandler================"+serviceName);
        String version = ChargingPileServer.getVersion(handler.getName());
        Map<String, Object> req = analysis(data, serviceName, version);
        int seq = HexUtils.toInteger(data, 2, 4);
        req.put("seqint", seq);
        String pileNo = (String) req.get("pileNo");
        //log.error("==========ChargingPileBinaryHandler====="+serviceName+"======req====="+req.toString());
        ServiceParameter sp = new ServiceParameter(serviceName, pileNo, req);
        ServiceResult result = servicemainLogic.process(sp);
        String resultCode = result.getCode();
        String pilekey = "pile:".concat(pileNo);
        Map<String, Object> cachePile = REDIS.getCacheMap(pilekey);
        String tcfgkey = "pile:".concat(pileNo).concat(".tcfg");
        if (SERVICE_REGISTER.equals(serviceName) && OK.equals(resultCode)) {
            REDIS.setCacheObject(tcfgkey, false);
            regHandler(handler, pileNo, req);
        } else if (SERVICE_RMR.equals(serviceName) && OK.equals(resultCode)) {
            setCachePileRM(pilekey);
        }
        if (result.getBinary() != null) {
            String gunId = (String) req.get("gunId");
            if (gunId != null) {
                hori2(pileNo, gunId);
            }
            if (!handler.isOpen()) {
                log.error("send message failed. [{}]({}) connection lost", handler.getName(), pileNo);
                removeHandler(pileNo);
            } else {
                log.debug("server send msg >>>> ({}) |{}|", pileNo, toHex(result.getBinary()));
                handler.sendClientBinary(result.getBinary());
                XhpcDeviceMessage deviceMessage = new XhpcDeviceMessage();
                deviceMessage.setType(StationDeviceEnum.PILE.getCode());
                deviceMessage.setSerialNumber(sp.getPileNo());
                deviceMessage.setRemark("平台下发/回复充电桩");
                deviceMessage.setStatus(0);
                deviceMessage.setContent(HexUtils.toHex(result.getBinary()));
                deviceMessage.setChargeOrderNo((String) req.get("orderNo"));
                xhpcDeviceMessageMapper.insertByDomain(deviceMessage);
            }
            if (SERVICE_HB.equals(serviceName)) {
                if (OK.equals(resultCode)) {
                    pileNo = ChargingPileServer.getPileNo(handler);
                    Boolean tcfg = REDIS.getCacheObject(tcfgkey);
                    if (tcfg == null || !tcfg) {
                        String skey = pilekey.concat(".seqhex");
                        Thread.sleep(500);
                        Date date = Calendar.getInstance().getTime();
                        String timebin = getTimeBin(seqHex(skey), pileNo, date);
                        cachePile.put("hbConfTime", DateUtil.format(date, NORM_DATETIME_FORMAT));
                        REDIS.setCacheMap(pilekey, cachePile);
                        log.debug("server send time config msg >>>> ({}) |{}|", pileNo, timebin);
                        if (!handler.isOpen()) {
                            log.error("send message failed. [{}]({}) connection lost", handler.getName(), pileNo);
                            removeHandler(pileNo);
                        } else {
                            handler.sendClientBinary(HexUtils.toBytes(timebin));
                            if (gunId != null) {
                                hori2(pileNo, gunId);
                            }
                            REDIS.setCacheObject(tcfgkey, true, 1L, TimeUnit.DAYS);
                        }
                    }
                } else {
                    handler.forceClose();
                }

            }
        }
    }

    public static String getTimeBin(String seqhex, String pileNo, Date date) {

        String timebin = "6812".concat(seqhex).concat("0056").concat(pileNo).concat(toCp56Hex(date));
        timebin = timebin.concat(CRCCalculator.calcCrc(timebin));
        return timebin;
    }

    private void regHandler(ClientHandler handler, String pileNo, Map<String, Object> req) {

        ChargingPileServer.putHandler(pileNo, handler);
        ChargingPileServer.putVersion(handler.getName(), (String) req.get("version"));
    }

    private void setCachePileRM(String pilekey) {

        Map<String, Object> cachePile = REDIS.getCacheMap(pilekey);
        ChargingStationDto cacheStation = REDIS.getCacheObject("station:".concat(cachePile.get("stationId").toString()));
        cachePile.put("rateModelId", cacheStation.getRateModelId());
        REDIS.setCacheMap(pilekey, cachePile);
        log.debug("[{}] rate model[{}] responds success√", pilekey, cacheStation.getRateModelId());
    }

    private List<byte[]> parseDataList(byte[] data) {

        List<byte[]> dataList = new ArrayList<>();
        int processedLen, len = HexUtils.toInteger(data, 1, 2) + 4;
        processedLen = 0;
        int start = 0;
        while (start < data.length) {
            if (len > data.length) {
                String hex = toHex(data);
                if (!hex.startsWith("25", 10)) {
                    log.error("104incorrect input data|{}| len[{}]", hex, data.length);
                    break;
                }
            }
            dataList.add(ArrayUtils.subarray(data, start, start + len));
            processedLen += len;
            start = processedLen;
            len = HexUtils.toInteger(data, start + 1, start + 2) + 4;
        }
        if (dataList.size() > 1) {
            log.debug("detected stick package size[{}] [{}]", dataList.size(), toHex(data));
        }
        return dataList;
    }

    private Map<String, Object> analysis(byte[] data, String service, String ver) throws TxException {
        //System.out.println("===========ver========"+ver);
        //System.out.println("===========service========"+service);
        List<ServiceField> fieldList = FieldLogic.fieldList(ver, service);

        if (fieldList == null || fieldList.isEmpty())
            throw TxException.INNER_ERROR("field mapper not found");
        Map<String, Object> result = new HashMap<String, Object>();
        int pos = 6;
        for (ServiceField field : fieldList) {
            if (pos >= data.length || pos + field.getLen() > data.length)
                break;
            switch (field.getDataType()) {
                case DATA_TYPE_STRING:
                    result.put(field.getCode(), HexUtils.toString(data, pos, pos + field.getLen()));
                    break;
                case DATA_TYPE_INT:
                    result.put(field.getCode(), HexUtils.byte2int(ArrayUtils.subarray(data, pos, pos + field.getLen())));
                    break;
                case DATA_TYPE_LONG:
                    result.put(field.getCode(), HexUtils.byte2Long(ArrayUtils.subarray(data, pos, pos + field.getLen())));
                    break;
                default:
                    result.put(field.getCode(), toHex(data, pos, pos + field.getLen()));
            }
            pos += field.getLen();
        }
        result.put("hex", toHex(data));
        result.put("seqhex", getSeq(data));
        return result;
    }

    private String getSeq(byte[] data) {

        return toHex(ArrayUtils.subarray(data, 3, 5));
    }

    public static void main(String[] args) {
//        byte[] data ={104, 34, 0, -125, 0, 1, -128, -125, 96, 0, 6, 0, 1, 1, 2, 12, 78, 71, 53, 46, 52, 46, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -89, -69};
//
//        byte[] datas ={100,101,102,103,104,105,106,107,108,10,11,12,13,14,15,16,17,18,19,109};
//        int i = HexUtils.toInteger(datas, 1, 2) ;
//
//        System.out.println(i);

        String dataStr ="68A700C0003B80836000180001012309081438500002808360001800010150C3260E0809175898270E080917C027090000000000000000000000000000350C0000000000000000000000000040420F00000000000000000000000000804F12000000000000000000000000005C3B0400005C3B040000000000000000000000000000534149544552303030303030303030303101409C270E08091740000000000000000000660100006D70";
        String crc ="6D70";
        boolean b = dataStr.endsWith(crc);

        System.out.println("==b=="+b);


    }


}
