package com.xhpc.pp.logic;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xhpc.common.api.EvcsService;
import com.xhpc.common.api.PileOrderService;
import com.xhpc.common.api.SmsService;
import com.xhpc.common.core.domain.R;
import com.xhpc.common.data.redis.CacheOrderData;
import com.xhpc.common.data.redis.CacheRealtimeData;
import com.xhpc.common.data.up.RealtimeData;
import com.xhpc.common.enums.StationDeviceEnum;
import com.xhpc.evcs.dto.ChargeDetails;
import com.xhpc.pp.controller.ChargingController;
import com.xhpc.pp.domain.XhpcDeviceMessage;
import com.xhpc.pp.mapper.XhpcDeviceMessageMapper;
import com.xhpc.pp.tx.ServiceParameter;
import com.xhpc.pp.tx.ServiceResult;
import com.xhpc.pp.tx.logic.ServiceLogic;
import com.xhpc.pp.utils.HexUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.time.Duration;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
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.pp.server.ChargingPileServer.default_version;
import static com.xhpc.pp.utils.HexUtils.reverseHexInt;
import static com.xhpc.pp.utils.HexUtils.toBits;
import static com.xhpc.pp.utils.security.CacheDataUtils.reflectTranslate;

@Lazy
@Component("RealtimeDataLogic")
public class RealtimeDataLogic implements ServiceLogic {


    @Autowired
    private SmsService smsService;
    @Autowired
    private PileOrderService pileOrderService;
    @Autowired
    private ChargingController chargingController;
    @Resource
    XhpcDeviceMessageMapper deviceMessageMapper;
    @Resource
    EvcsService evcsService;

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

    public static final String[] stable = {"离线", "故障", "空闲", "充电中" ,"插枪"};
    public static final String[] pvgstable = {"否", "是", "未知"};
    private static final String[] hftable = {"急停按钮动作故障", "无可用整流模块", "出风口温度过高", "交流防雷故障", "交直流模块DC20通信中断", "绝缘检测模块FC08通信中断",
            "电度表通信中断", "读卡器通信中断", "RC10通信中断", "风扇调速板故障", "直流熔断器故障", "高压接触器故障", "门打开"};

    private final ExecutorService executorService = Executors.newFixedThreadPool(40);

    @Override
    public ServiceResult service(ServiceParameter sp) throws Exception {

        Map<String, Object> req = sp.getParameters();
        ObjectMapper objectMapper = new ObjectMapper();
        RealtimeData realtimeData = objectMapper.convertValue(req, RealtimeData.class);
        String pileNo = realtimeData.getPileNo();
        String gunId = realtimeData.getGunId();
        String terminalId = pileNo.concat(gunId);
        String gunkey = "gun:".concat(terminalId);
        Map<String, Object> cacheGun = REDIS.getCacheMap(gunkey);
        int statusInt = Integer.parseInt(realtimeData.getStatus());
        String statusplain = stable[statusInt];
        cacheGun.put("pileGunStatus", stable[Integer.parseInt(realtimeData.getPileGunStatus())]);
        cacheGun.put("vehicleGunStatus", pvgstable[Integer.parseInt(realtimeData.getVehicleGunStatus())]);

        double wv = reverseHexInt(realtimeData.getWorkingVoltage()) / 10.0;
        cacheGun.put("voltage", wv);
        double wc = reverseHexInt(realtimeData.getWorkingCurrent()) / 10.0;
        cacheGun.put("current", wc);
        String hardwareFault = realtimeData.getHardwareFault();
        String hfs = toBits(hardwareFault);
        char[] hfcs = hfs.toCharArray();
        String hfsplain = "";
        for (int i = hfcs.length - 1; i >= 0; i--) {
            if (hfcs[i] == 49) {
                int idx = hfcs.length - i - 1;
                if (idx < hftable.length) {
                    hfsplain = hfsplain.concat(hftable[idx]).concat("！");
                } else {
                    hfsplain = hfsplain.concat("未知硬件故障码idx[" + idx + "]").concat("..");
                }
            }
        }
        cacheGun.put("hf", hfsplain);
        String orderNo = realtimeData.getOrderNo();
        String orderkey = "order:".concat(orderNo);
        String soc = realtimeData.getSoc();
        String pkey = "pile:".concat(pileNo);
        Map<String, Object> cachePile = REDIS.getCacheMap(pkey);
        String statusOrSOC;
        Integer socInt = Integer.parseInt(soc, 16);
        if (statusplain.equals("充电中")) {
            statusOrSOC = socInt.toString();
        } else {
            statusOrSOC = statusplain;
        }
        Integer tr = reverseHexInt(realtimeData.getRemainingTime());
        cacheGun.put("statusInt", statusInt);
        cacheGun.put("status", statusOrSOC);
        cacheGun.put("remainingTime", tr);
        String stationTermStatusKey = "stationTerminalStatus:".concat(cachePile.get("stationId").toString());
        Map<String, Object> cacheTerminalStatusMap = REDIS.getCacheMap(stationTermStatusKey);
        if("空闲".equals(stable[Integer.parseInt(realtimeData.getPileGunStatus())]) && "是".equals(pvgstable[Integer.parseInt(realtimeData.getVehicleGunStatus())]) && !statusplain.equals("充电中")){
            cacheTerminalStatusMap.put(terminalId, "插枪");
            cacheGun.put("pileGunStatus", "插枪");
            REDIS.setCacheMap(stationTermStatusKey, cacheTerminalStatusMap);
        }else{
            cacheTerminalStatusMap.put(terminalId, statusOrSOC);
            //插枪枪状态发生改变
            REDIS.setCacheMap(stationTermStatusKey, cacheTerminalStatusMap);
        }

        Map<String, Object> cacheOrder = REDIS.getCacheMap(orderkey);
        if (!orderNo.equals("00000000000000000000000000000000")) {
            cacheOrder.put("hf", hfsplain);
            if (statusInt == 3) {
                Boolean o = (Boolean) cacheGun.get("ac.on");
                if (o != null && !o) {
                    R r = pileOrderService.pileStartup(orderNo, 1, "启动充电成功");
                    if (r.getCode() == 200) {
                        cacheGun.put("ac.on", true);
                        cacheGun.put("orderkey","order:"+orderNo);
                        final String orderstarttime = DateUtil.format(Calendar.getInstance().getTime(), NORM_DATETIME_FORMAT);
                        cacheGun.put("pileStartTime", orderstarttime);
                    }
                }
                List<CacheRealtimeData> realtimeDataList = (List<CacheRealtimeData>) cacheOrder.get("realtimeDataList");
                cacheGun.put("idleCnt", 0);
                if (realtimeDataList == null) {
                    realtimeDataList = new ArrayList<>();
                }
                Integer balance = (Integer) cacheOrder.get("initBalance");
                CacheRealtimeData cacheRealtimeData = translate(realtimeData);

                cacheRealtimeData.setCreateTime(DateUtil.now());
                String lord = orderkey.concat(".lord");
                REDIS.setCacheObject(lord, cacheRealtimeData);
                realtimeDataList.add(cacheRealtimeData);
                Integer cacheStartSoc = (Integer) cacheOrder.get("startSoc");
                if (cacheStartSoc == null && socInt != 0) cacheOrder.put("startSoc", socInt);
                if (socInt != 0) cacheOrder.put("endSoc", socInt);
                if (cachePile.get("version").equals("0C")) {
                    final String hex = realtimeData.getHex();
                    String cdhex = hex.substring(0x40 * 2 + 4, hex.length() - 4);
                    if (cdhex.length() > 0) {
                        cacheOrder.put("em1", calcem1(cdhex));
                    }
                }
                balance -= cacheRealtimeData.getAmountCharged();
                cacheOrder.put("rbalance", balance);
                cacheOrder.put("remainingTime", tr);
                cacheOrder.put("status", statusplain);
                cacheOrder.put("totalPower", cacheRealtimeData.getChargingDegree() / 10000.0);
                cacheOrder.put("totalMoney", cacheRealtimeData.getAmountCharged() / 10000.0);
                cacheOrder.put("realtimeDataList", realtimeDataList);
                cacheOrder.put("lastOrderTime", DateUtil.now());
                R r1 = pileOrderService.pileRimeOrder(orderNo);
                if (r1 == null || (r1.getMsg()!=null && r1.getMsg().contains("无效订单")) || (r1.getCode() >=500 && r1.getCode() != 3886)) {
                    chargingController.stopInvalidOrder(orderNo);
                } else {
                    Integer vul = (Integer) cachePile.get("voltageUpperLimits");
                    Integer cul = (Integer) cachePile.get("currentLimit");
                    if(vul != null  && cul != null && (wc > cul || wv > vul)) {
                        System.out.println("==========================wc "+wc);
                        System.out.println("==========================cul "+cul);
                        System.out.println("==========================wv "+wv);
                        System.out.println("==========================vul "+pileNo);
                        System.out.println("==========================wc > cul || wv > vul==================停止 充电=================="+pileNo);
                        R r = chargingController.stopCharging(pileNo, gunId, default_version);
                        if (r.getCode() == 200) {
                            Integer vcpcnt = (Integer) cacheOrder.get("vcpcnt");
                            cacheOrder.put("vcpcnt", vcpcnt == null ? 1 : vcpcnt++);
                            log.error("{} alert: v[{}], c[{}]", orderNo, wv, wc);
                        }
                    } else {
                        Integer stopSoc = (Integer) cacheOrder.get("stopSoc");
                        if (stopSoc != null && socInt >= stopSoc) {
                            R r = chargingController.stopCharging(pileNo, gunId, default_version);
                            String alerted = (String) cacheOrder.get("socalerted");
                            String tel = (String) cacheOrder.get("tel");
                            if (alerted == null && tel != null) {
                                if (r.getCode() == 200) {
                                    String phoneObject = REDIS.getCacheObject(tel+":"+stopSoc.toString());
                                    if (phoneObject == null || "".equals(phoneObject)) {
//                                        HashMap<String, String> paramMap = new HashMap<>();
//                                        paramMap.put("battery", stopSoc.toString());
//                                        paramMap.put("phone", tel);
//                                        paramMap.put("content", "【小华充电】尊敬的用户，你的车辆已充电达至设定的SOC(" + stopSoc + "%)。");
//                                        smsService.sendNotice(paramMap);
//                                        cacheOrder.put("socalerted", "true");
//                                        REDIS.setCacheObject(tel+":"+stopSoc.toString(), stopSoc.toString(), 300L, TimeUnit.SECONDS);
                                        pileOrderService.constantSoc(orderNo,stopSoc.toString());
                                    }
                                }
                            }
                        }
                        Integer chargingDegree = cacheRealtimeData.getChargingDegree();
                        Integer amountCharged =cacheRealtimeData.getAmountCharged();
                        if(chargingDegree < 10000 && amountCharged > 200000){
                            // 充电度数小于1，并且，充电金额大于10
                            R r = chargingController.stopCharging(pileNo, gunId, default_version);
                        }

                        if (balance < 50000) {
                            String alerted = (String) cacheOrder.get("lt5alerted");
                            String tel = (String) cacheOrder.get("tel");
                            if (alerted == null && tel != null) {
                                HashMap<String, String> paramMap = new HashMap<>();
                                paramMap.put("money", "5");
                                paramMap.put("phone", tel);
                                paramMap.put("content", "【翔桦充电】尊敬的用户，你的账户余额小于5元,为不影响您的正常充电，请您尽快充值交费，谢谢。");
                                smsService.sendNotice(paramMap);
                                cacheOrder.put("lt5alerted", "true");
                            }
                        }
                        if ("true".equals(cacheOrder.get("lordiss"))) {
                            cacheOrder.put("lordiss", "false");
                            pileOrderService.pileStop(orderNo, 4, "订单实时数据恢复");
                        }
                        // 判断金额和电量为0， 防止白嫖
                        if(realtimeDataList.size() > 9){
                            if(cacheRealtimeData.getAmountCharged() < 0.1 && cacheRealtimeData.getChargingDegree() < 0.1){
                                log.error("实时数据充电度数：{}, 实时充电金额为： {},订单[{}] 自动结束",
                                        cacheRealtimeData.getChargingDegree(),
                                        cacheRealtimeData.getAmountCharged(),
                                        cacheRealtimeData.getOrderNo());
                                chargingController.stopCharging(pileNo, gunId, default_version);
                            }
                        }
                    }
                }
            } else {
                if (cacheGun.get("orderkey") == null) {
                    cacheGun.put("orderkey", orderkey);
                }
                Integer errorfreecnt = (Integer) cacheOrder.get("errorfreecnt");
                errorfreecnt = errorfreecnt == null ? 1 : ++errorfreecnt;
                if (errorfreecnt == 2) {
                    CacheRealtimeData cacheRData = REDIS.getCacheObject(orderkey.concat(".lord"));
                    R r;
                    if (cacheRData == null) {
                        r = pileOrderService.abnormalOrder(orderNo);
                        cacheOrder.put("abnormal", "1.1 lord is null");
                        log.error("abnormal.1 order[{}]", orderNo);
                    } else {
                        CacheOrderData lordAsOd = new CacheOrderData(cacheRData, (String) cacheOrder.get("pileStartTime"),
                                (Integer) cacheOrder.get("startSoc"), (Integer) cacheOrder.get("stopSoc"));
                        cacheOrder.put("orderData", lordAsOd);
                        r = pileOrderService.pileEndOrder(orderNo);
                        REDIS.setCacheMap(orderkey, cacheOrder);
                        cacheOrder.put("abnormal", "1.2 lord as orderData");
                        log.error("abnormal.1.2 order[{}]", orderNo);
                    }
                    chargingController.stopCharging(pileNo, gunId, default_version);
                    cacheOrder.put("status", "空闲>2下发中止");
                    if (r == null || r.getCode() != 200) {
                        log.error("errorfreecnt r{}", JSONUtil.toJsonStr(r));
                        errorfreecnt = 1;
                    }
                }
                cacheOrder.put("errorfreecnt", errorfreecnt);
            }
            REDIS.setCacheMap(orderkey, cacheOrder);
        } else {
            if (statusInt == 3) {
                chargingController.stopCharging(pileNo, gunId, default_version);
                Integer bpcnt = (Integer) cacheGun.get("pleek.bp.cnt");
                cacheGun.put("pleek.bp.cnt", bpcnt==null?1:bpcnt++);
                cacheGun.put("pleek.bp.lt", DateUtil.now());
                orderkey = (String) cacheGun.get("orderkey");
                if (orderkey!=null) {
                    cacheGun.put("pleek.bp.lo", orderkey);
                    REDIS.setCacheMapValue(orderkey, "stopReason.pleek", "bp");
                }
            }
        }
        REDIS.setCacheMap(gunkey, cacheGun);

        XhpcDeviceMessage deviceMessage = new XhpcDeviceMessage();
        deviceMessage.setType(StationDeviceEnum.PILE.getCode());
        deviceMessage.setSerialNumber(sp.getPileNo());
        deviceMessage.setRemark("实时数据");
        deviceMessage.setStatus(0);
        deviceMessage.setContent((String) req.get("hex"));
        deviceMessage.setChargeOrderNo((String) req.get("orderNo"));
        deviceMessage.setGunStatus(Integer.parseInt(realtimeData.getVehicleGunStatus()));
        deviceMessage.setGunNumber(terminalId);
        deviceMessageMapper.insertByDomain(deviceMessage);
        //实时数据推送设备充电中状态变化
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                REDIS.setCacheObject("cdjgpc:realTime:"+orderNo,orderNo);
                //插枪状态发生变化进行判断是否收停车费
                String vehicleGunStatus = pvgstable[Integer.parseInt(realtimeData.getVehicleGunStatus())];
                if("否".equals(vehicleGunStatus)){
                    pileOrderService.getXhpcPlaceholderOrderTime(terminalId);
                }
            }
        });
        return new ServiceResult(false);
    }

    public static List<ChargeDetails> calcem2(String hex) {

        int sumPeriod = Integer.parseInt(hex.substring(0, 2), 16);
        List<ChargeDetails> cds = new ArrayList<>();
        int idx = 0;
        for (int i = 2; i < 2 + sumPeriod * 16; i = i + 16) { // todo
            ChargeDetails cd = new ChargeDetails();
            cd.setIdx(idx);
            idx++;
            cd.setDetailPower(HexUtils.reverseHexInt(hex.substring(i, i + 8)) / 100000.0);
            cd.setDetailElecMoney(HexUtils.reverseHexInt(hex.substring(i + 8, i + 16)) / 100000.0);
//            cd.setDetailSeviceMoney(HexUtils.reverseHexInt(hex.substring(i + 60, i + 68)) / 10000.0);
            cds.add(cd);
        }
        return cds;
    }

    public static List<ChargeDetails> calcem1(String hex) {

        int sumPeriod = Integer.parseInt(hex.substring(28, 30), 16);
        List<ChargeDetails> cds = new ArrayList<>();
        int idx = 0;
        for (int i = 38; i < 38 + sumPeriod * 16; i = i + 16) { // todo
            ChargeDetails cd = new ChargeDetails();
            cd.setIdx(idx);
            idx++;
            cd.setDetailPower(HexUtils.reverseHexInt(hex.substring(i, i + 8)) / 100000.0);
            cd.setDetailElecMoney(HexUtils.reverseHexInt(hex.substring(i + 8, i + 16)) / 100000.0);
//            cd.setDetailSeviceMoney(HexUtils.reverseHexInt(hex.substring(i + 60, i + 68)) / 10000.0);
            cds.add(cd);
        }
        return cds;
    }

    private CacheRealtimeData translate(RealtimeData realtimeData) throws InvocationTargetException, IllegalAccessException,
            InstantiationException {

        Class<CacheRealtimeData> crdclz = CacheRealtimeData.class;
        Class<RealtimeData> rdclz = RealtimeData.class;
        Field[] crdfields = crdclz.getDeclaredFields();
        return (CacheRealtimeData) reflectTranslate(realtimeData, crdclz, rdclz, crdfields);
    }

}
