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.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.evcs.dto.ChargeDetails;
import com.xhpc.pp.controller.ChargingController;
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 java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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.CP56Time2a.cp56toDateStr;
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;

    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通信中断", "风扇调速板故障", "直流熔断器故障", "高压接触器故障", "门打开"};


    @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", pvgstable[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);
        cacheTerminalStatusMap.put(terminalId, statusOrSOC);
        REDIS.setCacheMap(stationTermStatusKey, cacheTerminalStatusMap);
        Map<String, Object> cacheOrder = REDIS.getCacheMap(orderkey);
        if (!orderNo.equals("00000000000000000000000000000000")) {
            if (statusInt == 3) {
                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);
                balance -= cacheRealtimeData.getAmountCharged();
                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("0B")) {
                    final String hex = realtimeData.getHex();
                    String cdhex = hex.substring(0x40 * 2 + 4, hex.length() - 4);
                    if (cdhex.length() > 0) {
                        cacheOrder.put("em1", calcem(cdhex));
                    }
                }
                cacheOrder.put("rbalance", balance);
                cacheOrder.put("remainingTime", tr);
                cacheOrder.put("rdtime", DateUtil.calendar().getTime().getTime());
                cacheOrder.put("status", statusplain);
                cacheOrder.put("totalPower", cacheRealtimeData.getChargingDegree() / 10000.0);
                cacheOrder.put("totalMoney", cacheRealtimeData.getAmountCharged() / 10000.0);
                cacheOrder.put("realtimeDataList", realtimeDataList);
                pileOrderService.pileRimeOrder(orderNo);
                Integer vul = (Integer) cachePile.get("voltageUpperLimits");
                Integer cul = (Integer) cachePile.get("currentLimit");
                if (wc > cul || wv > vul) {
                    R r = chargingController.nativeStopCharging(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.nativeStopCharging(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) {
                                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");
                            }
                        }
                    }
                    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, "订单实时数据恢复");
                    }
                }
            } else {
                Integer errorfreecnt = (Integer) cacheOrder.get("errorfreecnt");
                errorfreecnt = errorfreecnt == null ? 1 : ++errorfreecnt;
                if (errorfreecnt == 2) {
                    CacheRealtimeData cacheRData = REDIS.getCacheObject(orderkey.concat(".lord"));
                    R r = null;
                    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);
                        cacheOrder.put("status", "已结束");
                    }
                    if (r == null || r.getCode() != 200) {
                        log.error("errorfreecnt r{}", JSONUtil.toJsonStr(r));
                        errorfreecnt = 1;
                    }
                }
                cacheOrder.put("errorfreecnt", errorfreecnt);
            }
            REDIS.setCacheMap(orderkey, cacheOrder);
        } else {
            orderkey = (String) cacheGun.get("orderkey");
            Integer idleCnt = (Integer) cacheGun.get("idleCnt");
            if (orderkey != null && statusInt != 3) {
                idleCnt = idleCnt == null ? 0 : idleCnt;
                idleCnt++;
                if (idleCnt >= 2) {
                    if (orderkey.length() > 0) {
                        Map<String, Object> problematicOrder = REDIS.getCacheMap(orderkey);
                        CacheRealtimeData cacheRealtimeData = REDIS.getCacheObject(orderkey.concat(".lord"));
                        if (problematicOrder != null && cacheRealtimeData != null) {
                            Integer startSoc = (Integer) problematicOrder.get("startSoc");
                            Integer endSoc = (Integer) problematicOrder.get("endSoc");
                            problematicOrder.put("orderData", new CacheOrderData(cacheRealtimeData,
                                    (String) problematicOrder.get("startTime"),
                                    startSoc == null ? 0 : startSoc, endSoc == null ? 0 : endSoc));
                            log.error("abnormal.2.1 order[{}]", orderNo);
                            cacheOrder.put("abnormal", "2.1");
                            cacheOrder.put("status", "已结束");
                        } else {
                            log.error("abnormal.2.2 order[{}]", orderNo);
                            cacheOrder.put("abnormal", "2.2");//not respond
                            String pushOrderKey = orderkey.replace("order:", "pushOrder:");
                            if (REDIS.getCacheMap(pushOrderKey) != null) {
                                REDIS.setCacheMapValue(pushOrderKey, "startChargeSeqStat", 4);
                            }
                        }
                        orderNo = orderkey.replace("order:", "");
                        pileOrderService.abnormalOrder(orderNo);
                        cacheGun.put("orderkey", null);
                        idleCnt = 0;
                        cacheGun.put("idleCnt", idleCnt);
                    }
                }
            } else {
                idleCnt = 0;
                cacheGun.put("idleCnt", idleCnt);
            }
        }
        REDIS.setCacheMap(gunkey, cacheGun);
        return new ServiceResult(false);
    }

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

        int sumPeriod = Integer.parseInt(hex.substring(0, 2), 16);
        List<ChargeDetails> cds = new ArrayList<>();
        for (int i = 2; i < sumPeriod * 68 + 2; i = i + 68) {
            ChargeDetails cd = new ChargeDetails();
            cd.setDetailStartTime(cp56toDateStr(hex.substring(i, i + 14)));
            cd.setDetailEndTime(cp56toDateStr(hex.substring(i + 14, i + 28)));
            cd.setElecPrice(HexUtils.reverseHexInt(hex.substring(i + 28, i + 36)) / 100000.0);
            cd.setSevicePrice(HexUtils.reverseHexInt(hex.substring(i + 36, i + 44)) / 100000.0);
            cd.setDetailPower(HexUtils.reverseHexInt(hex.substring(i + 44, i + 52)) / 10000.0);
            cd.setDetailElecMoney(HexUtils.reverseHexInt(hex.substring(i + 52, i + 60)) / 10000.0);
            cd.setDetailElecMoney(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);
    }

}
