package com.xhpc.evcs.notification;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import com.xhpc.common.data.redis.CacheRateModel;
import com.xhpc.common.data.redis.CacheRealtimeData;
import com.xhpc.evcs.domain.AuthSecretToken;
import com.xhpc.evcs.dto.*;
import com.xhpc.evcs.jpa.AuthSecretTokenRepository;
import com.xhpc.evcs.jpa.OrderMappingRepository;
import com.xhpc.evcs.jpa.StatisticTimeIntervalRepository;
import com.xhpc.evcs.utils.DateUtil;
import com.xhpc.evcs.utils.JSONUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

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.*;

import static cn.hutool.core.util.NumberUtil.isInteger;
import static com.xhpc.common.data.redis.StaticBeanUtil.REDIS;
import static com.xhpc.evcs.domain.AuthSecretToken.SECRET_TOKEN_TYPE_OUT;
import static com.xhpc.evcs.utils.DateUtil.DATE_FORMAT_DATE_TIME;

@Component
public class NotificationEquipChargeStatusTask extends CoreDispatcher {

    @Autowired
    private AuthSecretTokenRepository authSecretTokenRepository;
    @Autowired
    private StatisticTimeIntervalRepository statisticTimeIntervalRepository;
    @Autowired
    private OrderMappingRepository orderMappingRepository;

    private final Logger logger = LoggerFactory.getLogger(NotificationEquipChargeStatusTask.class);

    /**
     * 推送充电状态
     * @throws IOException
     */
    //都开放
    @Scheduled(fixedRate = 1000 * 30)
    public void run() throws IOException {

        List<AuthSecretToken> authSecretTokenOutList = authSecretTokenRepository.findBySecretTokenType(SECRET_TOKEN_TYPE_OUT);
        final Collection<String> gunkeys = REDIS.keys("gun:*");
        for (AuthSecretToken authSecretToken : authSecretTokenOutList) {
            for (String gunkey : gunkeys) {
                if (!gunkey.endsWith(".seqdec") && !gunkey.endsWith(".seqhex") && !gunkey.endsWith(".hori")) {
                    final Map<String, Object> cacheGun = REDIS.getCacheMap(gunkey);
                    final String status = (String) cacheGun.get("status");
                    if (status != null) {
                        if(cacheGun.get("cycOrder") !=null){
                            //川逸充订单不走实时充电状态接口
                            continue;
                        }
                        if (isInteger(status)) {
                            String orderkey = (String) cacheGun.get("orderkey");
                            if (orderkey == null) {
                                logger.error("[{}] is charging but no okey in cache..", gunkey);
                                continue;
                            }
                            EquipChargeStatus equipChargeStatus = new EquipChargeStatus();
                            final String internetSerialNumber = REDIS.getCacheMapValue(orderkey.replace("order:", "pushOrder:"),
                                    "internetSerialNumber");
                            if (internetSerialNumber == null || !internetSerialNumber.startsWith(authSecretToken.getOperatorId3irdpty()) || "MA7D6FPW6".equals(authSecretToken.getOperatorId3irdpty())) {
                                continue;
                            }
                            equipChargeStatus.setStartChargeSeq(internetSerialNumber);
                            equipChargeStatus.setStartChargeSeqStat(2);
                            equipChargeStatus.setConnectorID(orderkey.substring(6, 22));
                            equipChargeStatus.setConnectorStatus(3);
                            equipChargeStatus.setCurrentA(REDIS.getCacheMapValue(gunkey, "current"));
                            equipChargeStatus.setVoltageA(REDIS.getCacheMapValue(gunkey, "voltage"));
                            Integer soc = REDIS.getCacheMapValue(orderkey, "endSoc");
                            equipChargeStatus.setSoc(soc == null ? 0.0 : (double) (soc > 100 ? 100 : soc));
                            CacheRealtimeData lord = REDIS.getCacheObject(orderkey.concat(".lord"));
                            String lordTime;
                            if (lord != null) {
                                lordTime = lord.getCreateTime();
                            } else {
                                lordTime = DateUtil.date2String(Calendar.getInstance().getTime(), DATE_FORMAT_DATE_TIME);
                            }
                            equipChargeStatus.setStartTime(REDIS.getCacheMapValue(orderkey, "startTime"));
                            equipChargeStatus.setEndTime(lordTime);
                            equipChargeStatus.setChargeModel(3);
                            equipChargeStatus.setTotalPower(REDIS.getCacheMapValue(orderkey, "totalPower"));
                            equipChargeStatus.setTotalMoney(REDIS.getCacheMapValue(orderkey, "totalMoney"));
                            final Long rateModelId = REDIS.getCacheMapValue(gunkey.replace("gun", "pile").substring(0, 19),
                                    "rateModelId");
                            final CacheRateModel cacheRateModel = REDIS.getCacheObject("rateModel:" + rateModelId);
                            calculateEm(equipChargeStatus, cacheRateModel);
                            notify(equipChargeStatus, authSecretToken);
                        }
                    }
                }
            }
        }
    }

    public static void calculateEm(EquipChargeStatus 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_DOWN).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(2, 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(2, 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((double) Math.round(powerPrice / 1000D) / 100D);
        cd.setSevicePrice((double) Math.round(svcPrice / 1000D) / 100D);
        final double detailElecMoney = BigDecimal.valueOf(cd.getElecPrice())
                .multiply(BigDecimal.valueOf(cd.getDetailPower()))
                .setScale(2, RoundingMode.HALF_DOWN).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_DOWN).doubleValue();
        cd.setDetailSeviceMoney(BigDecimal.valueOf(detailSeviceMoney).setScale(2, RoundingMode.HALF_DOWN).doubleValue());
    }

    public void notify(EquipChargeStatus equipChargeStatus, AuthSecretToken authSecretTokenOut) throws IOException {

        final String operatorId = authSecretTokenOut.getOperatorId3irdpty().equals("765367656") ?
                authSecretTokenOut.getOperatorId() : "MA6DFCTD5";
        if(operatorId.equals("765367656")){
            equipChargeStatus.setChargeModel(null);
        }
        String data = JSONUtil.toJSONString(equipChargeStatus);
        System.out.println("===============充电状态推送===========data============="+data+"=========startChargeSeq============"+equipChargeStatus.getStartChargeSeq());
        CommonRequest<CDChargeOrderInfo4BonusReq> commonRequest = new CommonRequest<>();
        commonRequest.setData(data);
        String responseBody = ok(commonRequest, "/notification_equip_charge_status", authSecretTokenOut);

        System.out.println("===============充电状态推送===========responseBody============="+responseBody+"=========startChargeSeq============"+equipChargeStatus.getStartChargeSeq());
        //System.out.println("===============充电结束状态推送========================");
        EquipChargeStatusRes equipChargeStatusRes = DTOJsonHelper.parseResponseData(responseBody,
                EquipChargeStatusRes.class, authSecretTokenOut);
        if (equipChargeStatusRes != null && equipChargeStatusRes.getSuccStat() != 0) {
            logger.error("push equipment order status [%s] failed: %s", equipChargeStatus.getStartChargeSeq(), responseBody);
        }
    }

}
