package com.xhpc.pp.controller;

import cn.hutool.core.date.DateUtil;
import com.xhpc.common.api.dto.ChargingStationDto;
import com.xhpc.common.core.domain.R;
import com.xhpc.common.core.utils.HttpUtils;
import com.xhpc.common.enums.StationDeviceEnum;
import com.xhpc.evcs.cdjgpc.dto.CDConnectorInfo;
import com.xhpc.evcs.cdjgpc.dto.CDEquipmentInfo;
import com.xhpc.evcs.domain.XhpcChargingPile;
import com.xhpc.pp.domain.XhpcDeviceMessage;
import com.xhpc.pp.logic.RateModelRequestLogic;
import com.xhpc.pp.logic.RemoteRebootDataLogic;
import com.xhpc.pp.logic.RemoteUpgradeDataLogic;
import com.xhpc.pp.logic.PileConfigDataLogic;
import com.xhpc.pp.mapper.XhpcDeviceMessageMapper;
import com.xhpc.pp.utils.HexUtils;
import lombok.extern.slf4j.Slf4j;
import org.quickserver.net.server.ClientHandler;
import org.springframework.data.domain.Example;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMAT;
import static cn.hutool.core.util.NumberUtil.isInteger;
import static com.xhpc.common.data.redis.StaticBeanUtil.REDIS;
import static com.xhpc.common.data.redis.StaticBeanUtil.seqHex;
import static com.xhpc.pp.controller.ChargingController.checkPile;
import static com.xhpc.pp.controller.ChargingController.checkPileConfig;
import static com.xhpc.pp.controller.ChargingController.fmt;
import static com.xhpc.pp.controller.ChargingController.getRR;
import static com.xhpc.pp.server.ChargingPileBinaryHandler.getTimeBin;
import static com.xhpc.pp.server.ChargingPileServer.getHandler;
import static com.xhpc.pp.server.ChargingPileServer.removeHandler;

@RestController
@Slf4j
public class PileController {


    @Resource
    XhpcDeviceMessageMapper deviceMessageMapper;

    private final String[] GUNNAMES = {"", "A", "B", "C", "D"};

    @PostMapping("pile/whitelist/add/{stationId}/{version}")
    public Object addWhitelist(@PathVariable("stationId") Long stationId,
                               @PathVariable("version") String version,
                               @RequestBody Set<String> pileNoSet) {

        Set<String> pileWhitelist = REDIS.getCacheSet("PILE_WHITELIST");
        pileWhitelist.addAll(pileNoSet);
        REDIS.setCacheSet("PILE_WHITELIST", pileWhitelist);
        String stationKey = "station:".concat(stationId.toString());
        ChargingStationDto cacheStation = REDIS.getCacheObject(stationKey);
        if (cacheStation == null) {
            cacheStation = new ChargingStationDto();
            cacheStation.setStationId(stationId);
        }
        Set<String> stationPiles = cacheStation.getPiles();
        if (stationPiles == null) {
            cacheStation.setPiles(pileNoSet);
        } else {
            cacheStation.getPiles().addAll(pileNoSet);
        }
        REDIS.setCacheObject(stationKey, cacheStation);
        for (String pileNo : pileNoSet) {
            String pkey = "pile:".concat(pileNo);
            Map<String, Object> cachePile = REDIS.getCacheMap(pkey);
            cachePile.put("stationId", stationId);
            cachePile.put("version", version);
            XhpcChargingPile pileExample = new XhpcChargingPile();
            pileExample.setSerialNumber(pileNo);
            //Example<XhpcChargingPile> example = Example.of(pileExample);
            XhpcChargingPile xhpcChargingPile = deviceMessageMapper.getXhpcChargingPile(pileNo);
            if (xhpcChargingPile != null) {
                cachePile.put("connectorType", xhpcChargingPile.getConnectorType() == null ? 4 :
                        xhpcChargingPile.getConnectorType());
                cachePile.put("voltageUpperLimits", xhpcChargingPile.getMaxVoltage().intValue());
                cachePile.put("voltageLowerLimits", xhpcChargingPile.getMinVoltage().intValue());
                cachePile.put("currentLimit", xhpcChargingPile.getCurrent());
                cachePile.put("power", xhpcChargingPile.getPower());
                cachePile.put("nationalStandard", xhpcChargingPile.getNationalStandard().equals("2011") ? 1 : 2);
                cachePile.put("equipmentType", xhpcChargingPile.getEquipmentType());
                REDIS.setCacheMap("pile:".concat(pileNo), cachePile);
                getEquipmentInfo(xhpcChargingPile);
            }
        }
        //添加桩和枪信息
        log.info("station [{}] pile whitelist add: [{}]", stationId, Arrays.toString(pileNoSet.toArray()));
        return R.ok();
    }

    @DeleteMapping("pile/whitelist/delete/{stationId}")
    public Object deleteWhitelist(@PathVariable("stationId") Long stationId, @RequestBody Set<String> pileNoSet) {

        REDIS.deleteSetVal("PILE_WHITELIST", pileNoSet.toArray());
        String stationKey = "station:".concat(stationId.toString());
        ChargingStationDto cacheStation = REDIS.getCacheObject(stationKey);
        cacheStation.getPiles().removeAll(pileNoSet);
        REDIS.setCacheObject(stationKey, cacheStation);
        log.info("station [{}] pile whitelist delete: [{}]", stationId, Arrays.toString(pileNoSet.toArray()));
        return R.ok();
    }


    @PostMapping("pile/{pileNo}/timeNRateModel")
    public R configTimeNRateModel(@PathVariable("pileNo") String pileNo) {

        String pkey = "pile:".concat(pileNo);
        Map<String, Object> cachePile = REDIS.getCacheMap(pkey);
        R<Object> r = checkPile(cachePile);
        if (r.getCode() == 200) {
            String svcSrv = (String) cachePile.get("svcSrv");
            String response = HttpUtils.post(fmt(svcSrv).concat("/native/pile/").concat(pileNo).concat("/timeNRateModel"));
            r = getRR(response);
        }
        return r;
    }


    @PostMapping("native/pile/{pileNo}/rateModel")
    public Object configRateModel(@PathVariable("pileNo") String pileNo, @RequestBody String msg) {

        ClientHandler handler = getHandler(pileNo);
        R r;
        if (handler != null && handler.isOpen()) {
            if (!handler.isOpen()) {
                log.error("send message failed. [{}]({}) connection lost", handler.getName(), pileNo);
                removeHandler(pileNo);
                r = R.fail("充电桩连接已断开,请稍后再试");
            } else {
                try {
                    log.debug("[{}] - server send msg >>>> ({}) |{}|", handler.getName(), pileNo, msg);
                    handler.sendClientBinary(HexUtils.toBytes(msg));
                    r = R.ok(null, "网络延迟,请重试");
                } catch (IOException e) {
                    log.error("费率模型下发失败[{}]:".concat(e.getMessage()).concat(".无法启动充电"), pileNo);
                    removeHandler(pileNo);
                    r = R.fail("电桩网络通信失败,请重试");
                }
            }
        } else {
            log.error("费率模型下发失败[{}].无法启动充电", pileNo);
            r = R.fail("启动失败,请稍后重试");
        }
        return r;
    }

    @PostMapping("native/pile/{pileNo}/timeNRateModel")
    public R nativeConfigTimeNRateModel(@PathVariable("pileNo") String pileNo) {

        ClientHandler handler = getHandler(pileNo);
        R r = R.fail("校时校费下发失败,充电桩未注册,或场站费率未设置.");
        if (handler != null && handler.isOpen()) {
            if (!handler.isOpen()) {
                log.error("send message failed. [{}]({}) connection lost", handler.getName(), pileNo);
                removeHandler(pileNo);
                r = R.fail("充电桩连接已断开,请稍后再试.");
            } else {
                Collection<String> stationKeys = REDIS.keys("station:*");
                for (String stationKey : stationKeys) {
                    ChargingStationDto stationDto = REDIS.getCacheObject(stationKey);
                    Long rateModelIdStation = stationDto.getRateModelId();
                    if (rateModelIdStation != null) {
                        Set<String> pns = stationDto.getPiles();
                        if (pns != null) for (String pn : pns) {
                            if (pn.equals(pileNo)) {
                                String pk = "pile:".concat(pileNo);
                                String gkPattern = ("gun:").concat(pileNo).concat("*");
                                Collection<String> gks = REDIS.keys(gkPattern);
                                boolean charging = false;
                                for (String gk : gks) {
                                    if (gk.length() == 20 && isInteger(REDIS.getCacheMapValue(gk, "status"))) {
                                        charging = true;
                                        break;
                                    }
                                }
                                if (!charging) {
                                    String rsmsg = RateModelRequestLogic.translate(pileNo, rateModelIdStation, "0058",
                                            REDIS.getCacheMapValue(pk, "version"));
                                    try {
                                        handler.sendClientBinary(HexUtils.toBytes(rsmsg));
                                        log.info("pile[{}] rate model[{}] sent ⚪ {}", pileNo, rateModelIdStation, rsmsg);
                                        String configRmReply = pk.concat(".configRateReply");
                                        REDIS.setCacheObject(configRmReply, "已下发", 30L, TimeUnit.MINUTES);
                                        String skey = pk.concat(".seqhex");
                                        Thread.sleep(500);
                                        Date date = Calendar.getInstance().getTime();
                                        String timebin = getTimeBin(seqHex(skey), pileNo, date);
                                        handler.sendClientBinary(HexUtils.toBytes(timebin));
                                        REDIS.setCacheMapValue(pk, "manConfTime", DateUtil.format(date, NORM_DATETIME_FORMAT));
                                        String configTimeReply = pk.concat(".configTimeReply");
                                        REDIS.setCacheObject(configTimeReply, "已下发", 30L, TimeUnit.MINUTES);
                                        r = R.ok("校时校费下发成功.");

                                        XhpcDeviceMessage deviceMessage = new XhpcDeviceMessage();
                                        deviceMessage.setType(StationDeviceEnum.PILE.getCode());
                                        deviceMessage.setSerialNumber(pileNo);
                                        deviceMessage.setRemark("充电桩计费模型请求");
                                        deviceMessage.setStatus(0);
                                        deviceMessage.setContent(rsmsg);
                                        deviceMessageMapper.insertByDomain(deviceMessage);
                                    } catch (IOException | InterruptedException e) {
                                        r = R.fail("校时|校费下发失败,请重试.");
                                    }
                                } else {
                                    r = R.fail("校时校费下发失败,充电桩在充电中.");
                                }
                            }
                        }
                    }
                }
            }
        }
        return r;
    }


    @PostMapping("pile/{pileNo}/reboot")
    public R pileReboot(@PathVariable("pileNo") String pileNo) {

        String pkey = "pile:".concat(pileNo);
        Map<String, Object> cachePile = REDIS.getCacheMap(pkey);
        R<Object> r = checkPile(cachePile);
        if (r.getCode() == 200) {
            String svcSrv = (String) cachePile.get("svcSrv");
            String response = HttpUtils.post(fmt(svcSrv).concat("/native/pile/").concat(pileNo).concat("/reboot"));
            r = getRR(response);
        }
        return r;
    }


    @PostMapping("native/pile/{pileNo}/reboot")
    public R nativePileReboot(@PathVariable("pileNo") String pileNo) {

        ClientHandler handler = getHandler(pileNo);

        R r = R.fail("充电桩重启失败,充电桩未注册.");
        if (handler != null && handler.isOpen()) {
            if (!handler.isOpen()) {
                log.error("send message failed. [{}]({}) connection lost", handler.getName(), pileNo);
                removeHandler(pileNo);
                r = R.fail("充电桩连接已断开,请稍后再试.");
            } else {
                String gkPattern = ("gun:").concat(pileNo).concat("*");
                Collection<String> gks = REDIS.keys(gkPattern);
                boolean charging = false;
                for (String gk : gks) {
                    if (gk.length() == 20 && isInteger(REDIS.getCacheMapValue(gk, "status"))) {
                        charging = true;
                        break;
                    }
                }
                int runFlag = charging ? 2 : 1;
                try {
                    String rsmsg = RemoteRebootDataLogic.translate(pileNo, runFlag);

                    handler.sendClientBinary(HexUtils.toBytes(rsmsg));
                    r = R.ok("充电桩重启下发成功.");

                    XhpcDeviceMessage deviceMessage = new XhpcDeviceMessage();
                    deviceMessage.setType(StationDeviceEnum.PILE.getCode());
                    deviceMessage.setSerialNumber(pileNo);
                    deviceMessage.setRemark("充电桩主动下发远程重启");
                    deviceMessage.setStatus(0);
                    deviceMessage.setContent(rsmsg);
                    deviceMessageMapper.insertByDomain(deviceMessage);

                } catch (Exception e) {
                    r = R.fail("充电桩重启下发失败,请重试.");
                }

            }
        }
        return r;
    }


    @PostMapping("pile/{pileNo}/softwareUpgrade")
    public R pileSoftwareUpgrade(@PathVariable("pileNo") String pileNo) {

        String pkey = "pile:".concat(pileNo);
        Map<String, Object> cachePile = REDIS.getCacheMap(pkey);
        R<Object> r = checkPile(cachePile);
        if (r.getCode() == 200) {
            String svcSrv = (String) cachePile.get("svcSrv");
            String response = HttpUtils.post(fmt(svcSrv).concat("/native/pile/").concat(pileNo).concat("/softwareUpgrade"));
            r = getRR(response);
        }
        return r;
    }


    @PostMapping("native/pile/{pileNo}/softwareUpgrade")
    public R nativePileSoftwareUpgrade(@PathVariable("pileNo") String pileNo) {

        ClientHandler handler = getHandler(pileNo);

        R r = R.fail("软件更新失败,充电桩未注册,或场站费率未设置.");
        if (handler != null && handler.isOpen()) {
            if (!handler.isOpen()) {
                log.error("send message failed. [{}]({}) connection lost", handler.getName(), pileNo);
                removeHandler(pileNo);
                r = R.fail("充电桩连接已断开,请稍后再试.");
            } else {
                String gkPattern = ("gun:").concat(pileNo).concat("*");
                Collection<String> gks = REDIS.keys(gkPattern);
                boolean charging = false;
                for (String gk : gks) {
                    if (gk.length() == 20 && isInteger(REDIS.getCacheMapValue(gk, "status"))) {
                        charging = true;
                        break;
                    }
                }
                int runFlag = charging ? 2 : 1;
                try {
                    String rsmsg = RemoteUpgradeDataLogic.translate(pileNo,
                            "0", 0L,
                            "0", "0", "0",
                            runFlag);

                    handler.sendClientBinary(HexUtils.toBytes(rsmsg));
                    r = R.ok("软件更新下发成功.");

                    XhpcDeviceMessage deviceMessage = new XhpcDeviceMessage();
                    deviceMessage.setType(StationDeviceEnum.PILE.getCode());
                    deviceMessage.setSerialNumber(pileNo);
                    deviceMessage.setRemark("充电桩主动下发远程更新");
                    deviceMessage.setStatus(0);
                    deviceMessage.setContent(rsmsg);
                    deviceMessageMapper.insertByDomain(deviceMessage);

                } catch (Exception e) {
                    r = R.fail("软件更新下发失败,请重试.");
                }

            }
        }
        return r;
    }


    @PostMapping("pile/all/softwareUpgrade")
    public R pileAllUpgrade() {

        Collection<String> stationKeys = REDIS.keys("station:*");
        for (String stationKey : stationKeys) {
            ChargingStationDto stationDto = REDIS.getCacheObject(stationKey);
            Long rateModelIdStation = stationDto.getRateModelId();
            if (rateModelIdStation != null) {
                Set<String> pks = stationDto.getPiles();
                if (pks != null) for (String pileNo : pks) {
                    String gkPattern = ("gun:").concat(pileNo).concat("*");
                    Collection<String> gks = REDIS.keys(gkPattern);
                    boolean charging = false;
                    for (String gk : gks) {
                        if (gk.length() == 20 && isInteger(REDIS.getCacheMapValue(gk, "status"))) {
                            charging = true;
                            break;
                        }
                    }

                    ClientHandler handler = getHandler(pileNo);
                    if (handler != null && handler.isOpen()) {
                        String rsmsg = RemoteUpgradeDataLogic.translate(pileNo, "0", 0L, "0", "0", "0", charging ? 2 : 1);
                        try {
                            handler.sendClientBinary(HexUtils.toBytes(rsmsg));
                            log.info("pile[{}] soft upgrade sent ⚪ {}", pileNo, rsmsg);
                        } catch (IOException e) {
                            log.error("pile[{}] soft upgrade not sent ×", pileNo);
                        }
                    }
                }
            }
        }
        return R.ok();
    }


    @PostMapping("pile/all/reboot")
    public R pileAllReboot() {

        Collection<String> stationKeys = REDIS.keys("station:*");
        for (String stationKey : stationKeys) {
            ChargingStationDto stationDto = REDIS.getCacheObject(stationKey);
            Long rateModelIdStation = stationDto.getRateModelId();
            if (rateModelIdStation != null) {
                Set<String> pks = stationDto.getPiles();
                if (pks != null) for (String pileNo : pks) {
                    String gkPattern = ("gun:").concat(pileNo).concat("*");
                    Collection<String> gks = REDIS.keys(gkPattern);
                    boolean charging = false;
                    for (String gk : gks) {
                        if (gk.length() == 20 && isInteger(REDIS.getCacheMapValue(gk, "status"))) {
                            charging = true;
                            break;
                        }
                    }

                    ClientHandler handler = getHandler(pileNo);
                    if (handler != null && handler.isOpen()) {
                        String rsmsg = RemoteRebootDataLogic.translate(pileNo, charging ? 2 : 1);
                        try {
                            handler.sendClientBinary(HexUtils.toBytes(rsmsg));
                            log.info("pile[{}] reboot sent ⚪ {}", pileNo, rsmsg);
                        } catch (IOException e) {
                            log.error("pile[{}] reboot not sent ×", pileNo);
                        }
                    }
                }
            }
        }
        return R.ok();
    }


    /**
     * 工作参数设置
     * @param pileNo
     * @param powerNumber 下发功率百分比
     * @return
     */
    @PostMapping("pile/{pileNo}/{powerNumber}/configSettings")
    public R pileConfigSettings(@PathVariable("pileNo") String pileNo,@PathVariable("powerNumber") String powerNumber) {

        String pkey = "pile:".concat(pileNo);
        Map<String, Object> cachePile = REDIS.getCacheMap(pkey);
        R<Object> r = checkPileConfig(cachePile);
        if (r.getCode() == 200) {
            ClientHandler handler = getHandler(pileNo);
            r = R.fail("充电桩工作参数设置失败,充电桩未注册,或场站费率未设置.");
            if (handler != null && handler.isOpen()) {
                if (!handler.isOpen()) {
                    log.error("send message failed. [{}]({}) connection lost", handler.getName(), pileNo);
                    removeHandler(pileNo);
                    r = R.fail("充电桩连接已断开,请稍后再试.");
                } else {
                    try {
                        String rsmsg = PileConfigDataLogic.translate(pileNo,powerNumber);
                        handler.sendClientBinary(HexUtils.toBytes(rsmsg));
                        r = R.ok("充电桩主动下发充电桩工作参数下发成功.");
                        XhpcDeviceMessage deviceMessage = new XhpcDeviceMessage();
                        deviceMessage.setType(StationDeviceEnum.PILE.getCode());
                        deviceMessage.setSerialNumber(pileNo);
                        deviceMessage.setRemark("充电桩主动下发充电桩工作参数设置");
                        deviceMessage.setStatus(0);
                        deviceMessage.setContent(rsmsg);
                        deviceMessageMapper.insertByDomain(deviceMessage);
                    } catch (Exception e) {
                        r = R.fail("充电桩工作参数设置失败,请重试.");
                    }
                }
            }

        }
        return r;
    }

    //组装充电设备接口信息
    public void getEquipmentInfo(XhpcChargingPile xhpcChargingPile){
        String serialNumber = xhpcChargingPile.getSerialNumber();
        String skey = "cdjgpc:pile:".concat(serialNumber)+".notification";
        REDIS.deleteObject(skey);

        CDEquipmentInfo cdEquipmentInfo = new CDEquipmentInfo();
        cdEquipmentInfo.setEquipmentID(serialNumber);
        cdEquipmentInfo.setManufacturerID(xhpcChargingPile.getManufacturerId());
        cdEquipmentInfo.setManufacturerName(xhpcChargingPile.getManufactureName());
        cdEquipmentInfo.setEquipmentModel(xhpcChargingPile.getBrandModel());
        cdEquipmentInfo.setEquipmentProductCode(xhpcChargingPile.getEquipmentProductCode());
        cdEquipmentInfo.setEquipmentName(xhpcChargingPile.getManufactureName());
        cdEquipmentInfo.setTransformerID(xhpcChargingPile.getTransformerID());
        cdEquipmentInfo.setProductionDate(xhpcChargingPile.getProductionDate());
        cdEquipmentInfo.setConstructionTime(xhpcChargingPile.getConstructionTime());
        cdEquipmentInfo.setEquipmentType(xhpcChargingPile.getEquipmentType());
        cdEquipmentInfo.setEquipmentStatus(xhpcChargingPile.getEquipmentStatus());
        cdEquipmentInfo.setEquipmentPurpose(xhpcChargingPile.getEquipmentPurpose());
        cdEquipmentInfo.setEquipmentPower(xhpcChargingPile.getPower());
        cdEquipmentInfo.setNewNationalStandard(xhpcChargingPile.getNewNationalStandard());
        cdEquipmentInfo.setVinFlag(xhpcChargingPile.getVinFlag());
        cdEquipmentInfo.setConnectorInfos(getConnectorInfos(serialNumber,xhpcChargingPile));
        cdEquipmentInfo.setEquipmentClassificatlon(xhpcChargingPile.getEquipmentClassificatlon());
        REDIS.setCacheObject(skey, cdEquipmentInfo);

    }

    private List<CDConnectorInfo> getConnectorInfos(String pileNo, XhpcChargingPile xhpcChargingPile) {
        final Map<String, Object> cachePile = REDIS.getCacheMap("pile:".concat(pileNo));
        List<CDConnectorInfo> connectorInfoList = new ArrayList<>();
        Integer gunNumCache = (Integer) cachePile.get("gunNum");
        gunNumCache = gunNumCache == null ? 2 : gunNumCache;
        for (int i = 1; i <= gunNumCache; i++) {
            String gunId = pileNo.concat(String.format("%02d", i));

            CDConnectorInfo connectorInfo = new CDConnectorInfo();
            connectorInfo.setConnectorID(gunId);
            String connectorName = REDIS.getCacheMapValue("gun:".concat(gunId), "terminalName"); //todo set them in redis!
            connectorInfo.setConnectorName(connectorName == null ?
                    Integer.parseInt(pileNo.substring(10, 14)) + "号桩-" + GUNNAMES[i] +
                            "枪" : connectorName);
            Integer connectorType = (Integer) cachePile.get("connectorType");
            connectorInfo.setConnectorType(connectorType == null ? 4 : connectorType);
            Integer voltageUpperLimits = xhpcChargingPile.getMaxVoltage().intValue();
            connectorInfo.setVoltageUpperLimits(voltageUpperLimits == null ? 750 : voltageUpperLimits);
            Integer voltageLowerLimits = xhpcChargingPile.getMinVoltage().intValue();
            connectorInfo.setVoltageLowerLimits(voltageLowerLimits == null ? 300 : voltageLowerLimits);
            Integer current = (Integer) cachePile.get("current");
            connectorInfo.setCurrent(current == null ? 250 : current);
            Double power = xhpcChargingPile.getPower();
            connectorInfo.setPower(power == null ? 120.0 : power);
            Integer nationalStandard = (Integer) cachePile.get("nationalStandard");
            connectorInfo.setNationalStandard(nationalStandard == null ? 2 : nationalStandard);
            connectorInfo.setEquipmentClassification(xhpcChargingPile.getEquipmentClassificatlon());
            connectorInfo.setOpreateStatus(50);
            connectorInfo.setAuxPower(xhpcChargingPile.getAuxPower());
            connectorInfoList.add(connectorInfo);
        }
        return connectorInfoList;
    }
}
