package com.xhpc.evcs.cwj.api;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import com.xhpc.common.data.redis.CacheBmsReqChargerOutputData;
import com.xhpc.common.data.redis.CacheRateModel;
import com.xhpc.common.data.redis.CacheRealtimeData;
import com.xhpc.evcs.cwj.dto.CEJEquipChargeStatus;
import com.xhpc.evcs.domain.EtOrderMapping;
import com.xhpc.evcs.dto.*;
import com.xhpc.evcs.encryption.EvcsConst;
import com.xhpc.evcs.jpa.OrderMappingRepository;
import com.xhpc.evcs.jpa.XhpcHistoryOrderRepository;
import com.xhpc.evcs.utils.DateUtil;
import com.xhpc.evcs.utils.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.Map;
import java.util.Stack;

import static com.xhpc.common.data.redis.StaticBeanUtil.REDIS;
import static com.xhpc.evcs.notification.NotificationEquipChargeStatusTask.calculateEm;
import static com.xhpc.evcs.utils.DateUtil.orderNo2DateStr;

/**
 * @author yuyang
 * @Date 2025-07-16 15:30
 */
@Slf4j
@RestController()
public class CWJQueryEquipChargeStatusController {
    @Autowired
    XhpcHistoryOrderRepository xhpcHistoryOrderRepository;
    @Autowired
    OrderMappingRepository orderMappingRepository;

    /**
     * 查询充电状态
     * @param commonRequest
     * @return
     * @throws IOException
     */
    @PostMapping("/v40/query_equip_charge_status")
    public CommonResponse queryEquipChargeStatus(@RequestBody CommonRequest<ChargeInfoRequest> commonRequest) throws IOException {
        //创建数据实体类
        CEJEquipChargeStatus equipChargeStatus = new CEJEquipChargeStatus();
        //充电订单号 三方传进来都是27位
        String data = commonRequest.getData();
        ChargeInfoRequest chargeInfoRequest = JSONUtil.readParams(data, ChargeInfoRequest.class);
        CommonResponse response = new CommonResponse();
        if (chargeInfoRequest == null) {
            response.setRet(EvcsConst.RET_FAIL);
            response.setMsg("Request or token params validation failed");
        } else {
            String startChargeSeq = chargeInfoRequest.getStartChargeSeq();
            equipChargeStatus.setStartChargeSeq(startChargeSeq);
            //充电订单状态
            EtOrderMapping etOrderMapping = orderMappingRepository.findByEvcsOrderNo(startChargeSeq).orElse(null);
            if (etOrderMapping == null) {
                response.setRet(EvcsConst.RET_FAIL);
                response.setMsg("Order not found/started");
                log.error("3rdpty order[{}] not found", startChargeSeq);
                return response;
            }
            String internalOrderNum = etOrderMapping.getXhOrderNo();
            Map<String, Object> cacheOrder = REDIS.getCacheMap("order:" + internalOrderNum);
            if (cacheOrder == null) {
                response.setRet(EvcsConst.RET_FAIL);
                response.setMsg("Order cache data not found");
            } else {
                String orderStatus = (String) cacheOrder.get("status");
                int startChargeSeqStat;
                if (orderStatus == null) {
                    startChargeSeqStat = 5;
                } else switch (orderStatus) {
                    case "启动中":
                        startChargeSeqStat = 1;
                        break;
                    case "充电中":
                        startChargeSeqStat = 2;
                        break;
                    case "停止中":
                        startChargeSeqStat = 3;
                        break;
                    case "已结束":
                        startChargeSeqStat = 4;
                        break;
                    default:
                        startChargeSeqStat = 5;
                        break;
                }
                equipChargeStatus.setStartChargeSeqStat(startChargeSeqStat);
                //充电设备接口编码
                String connectorId = internalOrderNum.substring(0, 16);
                equipChargeStatus.setConnectorID(connectorId);
                //充电设备接口状态
                Map<String, Object> cacheGunData = REDIS.getCacheMap("gun:" + connectorId);
                String gunStatus = (String) cacheGunData.get("status");
                int connectorStatus = 0;
                switch (gunStatus) {
                    case "空闲":
                        connectorStatus = 1;
                        break;
                    case "离线":
                        connectorStatus = 2;
                        break;
                    case "故障":
                        connectorStatus = 255;
                        break;
                    default:
                        connectorStatus = 3;
                        break;
                }
                equipChargeStatus.setConnectorStatus(connectorStatus);
                //A相电流
                Double current = (Double) cacheGunData.get("current");
                equipChargeStatus.setCurrentA(current);
                //A相电压
                Double voltage = (Double) cacheGunData.get("voltage");
                equipChargeStatus.setVoltageA(voltage);
                //电池剩余电量
                Integer endSoc = (Integer) cacheOrder.get("endSoc");
                Double soc = Double.valueOf(endSoc == null ? 0 : endSoc);
                equipChargeStatus.setSoc(soc);
                //开始充电时间
                String startTime = (String) cacheOrder.get("startTime");
                if (startTime == null) startTime = orderNo2DateStr(internalOrderNum);
                equipChargeStatus.setStartTime(startTime);
                //本次采样时间 直接new一个当前时间的Date就可以了
                CacheRealtimeData lord = REDIS.getCacheObject("order:" + internalOrderNum + ".lord");
                if (lord != null) {
                    equipChargeStatus.setEndTime(lord.getCreateTime());
                    equipChargeStatus.setTotalMoney(lord.getAmountCharged() / 10000.0);
                    equipChargeStatus.setTotalPower(lord.getChargingDegree() / 10000.0);
                } else {
                    equipChargeStatus.setEndTime(equipChargeStatus.getStartTime());
                    equipChargeStatus.setTotalMoney(0.0);
                    equipChargeStatus.setTotalPower(0.0);
                }
                final Long rateModelId = REDIS.getCacheMapValue("pile:".concat(connectorId.substring(0, 14)), "rateModelId");
                final CacheRateModel cacheRateModel = REDIS.getCacheObject("rateModel:" + rateModelId);
                calculateEmYu(equipChargeStatus, cacheRateModel);

                CacheBmsReqChargerOutputData bms = REDIS.getCacheObject("order:"+internalOrderNum+".bms");
                if(bms==null || bms.getBmsEstRemainingTime()==null){
                    equipChargeStatus.setLeftTime(60);
                }else{
                    equipChargeStatus.setLeftTime(bms.getBmsEstRemainingTime());
                }

                response.setRet(EvcsConst.RET_SUCC);
                response.setData(JSONUtil.toJSONString(equipChargeStatus));
                response.setMsg("success");
            }
        }
        return response;
    }



    public static void calculateEmYu(CEJEquipChargeStatus equipChargeStatus, CacheRateModel cacheRateModel) {

        if (cacheRateModel == null) {
            equipChargeStatus.setChargeDetails(new ChargeDetails[0]);
            return;
        }
        final Date endTime = DateUtil.yyyyMMDDhhmmss2Date(equipChargeStatus.getEndTime());
        final Date startTime = DateUtil.yyyyMMDDhhmmss2Date(equipChargeStatus.getStartTime());
        long totalMilSec = endTime.getTime() - startTime.getTime();
        BigDecimal hours = new BigDecimal(totalMilSec).divide(new BigDecimal(3600000), 2, RoundingMode.FLOOR);
        int sumPeriod = hours.setScale(0, RoundingMode.CEILING).intValue() + 1;
        equipChargeStatus.setSumPeriod(sumPeriod);
        DateTime firstNDT = DateTime.of(startTime);
        Stack<ChargeDetails> chargeDetailsStack = new Stack<>();
        ChargeDetails chargeDetails = null;
        Double firstNElecMoney = 0.0;
        Double firstNSeviceMony = 0.0;
        Double firstNPower = 0.0;
        String rtfTemp = null;
        Instant startIst = startTime.toInstant();
        final Instant endInst = endTime.toInstant();
        Instant endPeriod;
        final Double totalPower = equipChargeStatus.getTotalPower();
        for (int i = 0; i < sumPeriod; i++) {
            if (!startIst.isBefore(endInst)) break;
            endPeriod = startIst.truncatedTo(ChronoUnit.HOURS).plusSeconds(3599L);
            if (!endPeriod.isBefore(endInst)) {
                endPeriod = endInst;
            }
            boolean newCd = false;
            int firstSTHourOfDay = firstNDT.getField(DateField.HOUR_OF_DAY);
            int tf = firstSTHourOfDay * 2;
            final String rtf = cacheRateModel.getTfPricesSeq()[tf];
            if (chargeDetails == null || !rtf.equals(rtfTemp)) {
                newCd = true;
                chargeDetails = new ChargeDetails();
            }
            rtfTemp = rtf;
            Duration durm = Duration.between(startIst, endPeriod);
            BigDecimal timePeriodRatio = BigDecimal.valueOf(durm.toMillis()).divide(BigDecimal.valueOf(totalMilSec), 2,
                    RoundingMode.HALF_UP);
            calculateCD(cacheRateModel, rtf, timePeriodRatio, BigDecimal.valueOf(totalPower), chargeDetails);
            if (i == 0) {
                chargeDetails.setDetailStartTime(equipChargeStatus.getStartTime());
            } else if (newCd) {
                chargeDetails.setDetailStartTime(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault()).format(startIst));
            }
            startIst = startIst.truncatedTo(ChronoUnit.HOURS).plusSeconds(3600L);
            chargeDetails.setDetailEndTime(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault()).format(endPeriod));
            if (newCd) {
                chargeDetailsStack.push(chargeDetails);
            }
        }
        ChargeDetails[] cda = new ChargeDetails[chargeDetailsStack.size()];
        equipChargeStatus.setChargeDetails(chargeDetailsStack.toArray(cda));
        equipChargeStatus.setSumPeriod(chargeDetailsStack.size());
        equipChargeStatus.setElecMoney(firstNElecMoney);
        final Double totalMoney = equipChargeStatus.getTotalMoney();
        for (ChargeDetails cd : chargeDetailsStack) {
            firstNElecMoney += cd.getDetailElecMoney();
            firstNSeviceMony += cd.getDetailSeviceMoney();
            firstNPower += cd.getDetailPower();
        }
        equipChargeStatus.setElecMoney(BigDecimal.valueOf(totalMoney).subtract(BigDecimal.valueOf(firstNSeviceMony)).setScale(2, RoundingMode.HALF_UP).doubleValue());
        equipChargeStatus.setSeviceMoney(BigDecimal.valueOf(firstNSeviceMony).setScale(2, RoundingMode.HALF_UP).doubleValue());
        final BigDecimal oddm =
                BigDecimal.valueOf(totalMoney).subtract(BigDecimal.valueOf(firstNElecMoney)).subtract(BigDecimal.valueOf(firstNSeviceMony));
        final BigDecimal oddp = BigDecimal.valueOf(totalPower).subtract(BigDecimal.valueOf(firstNPower));
        if (!chargeDetailsStack.isEmpty()) {
            ChargeDetails peek = chargeDetailsStack.peek();
            peek.setDetailElecMoney(BigDecimal.valueOf(peek.getDetailElecMoney()).add(oddm).setScale(2, RoundingMode.HALF_UP).doubleValue());
            peek.setDetailPower(BigDecimal.valueOf(peek.getDetailPower()).add(oddp).setScale(4, RoundingMode.HALF_UP).doubleValue());
        }
    }


    private static void calculateCD(CacheRateModel cacheRateModel, String rtf,
                                    BigDecimal timePeriodRatio, BigDecimal tpPower, ChargeDetails cd) {

        cd.setDetailPower(BigDecimal.valueOf(cd.getDetailPower() + tpPower.multiply(timePeriodRatio).doubleValue()).setScale(4, RoundingMode.HALF_UP).doubleValue());

        Integer powerPrice;
        Integer svcPrice;
        if ("00".equals(rtf)) {
            powerPrice = cacheRateModel.getT1Price();
            svcPrice = cacheRateModel.getT1SvcPrice();
        } else if ("01".equals(rtf)) {
            powerPrice = cacheRateModel.getT2Price();
            svcPrice = cacheRateModel.getT2SvcPrice();
        } else if ("02".equals(rtf)) {
            powerPrice = cacheRateModel.getT3Price();
            svcPrice = cacheRateModel.getT3SvcPrice();
        } else{
            powerPrice = cacheRateModel.getT4Price();
            svcPrice = cacheRateModel.getT4SvcPrice();
        }
        cd.setElecPrice( new BigDecimal(powerPrice).divide(new BigDecimal(100000)).setScale(4,RoundingMode.HALF_UP).doubleValue());
        cd.setSevicePrice(new BigDecimal(svcPrice).divide(new BigDecimal(100000)).setScale(4,RoundingMode.HALF_UP).doubleValue());
        final double detailElecMoney = BigDecimal.valueOf(cd.getElecPrice())
                .multiply(BigDecimal.valueOf(cd.getDetailPower()))
                .setScale(2, RoundingMode.HALF_UP).doubleValue();
        cd.setDetailElecMoney(BigDecimal.valueOf(detailElecMoney).setScale(2, RoundingMode.HALF_UP).doubleValue());
        final double detailSeviceMoney = BigDecimal.valueOf(cd.getSevicePrice())
                .multiply(BigDecimal.valueOf(cd.getDetailPower()))
                .setScale(2, RoundingMode.HALF_UP).doubleValue();
        cd.setDetailSeviceMoney(BigDecimal.valueOf(detailSeviceMoney).setScale(2, RoundingMode.HALF_UP).doubleValue());
    }
}
