package com.jsowell.pile.service.orderlogic;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.jsowell.adapay.service.AdapayService;
import com.jsowell.common.constant.CacheConstants;
import com.jsowell.common.constant.Constants;
import com.jsowell.common.core.domain.ykc.RealTimeMonitorData;
import com.jsowell.common.core.domain.ykc.TransactionRecordsData;
import com.jsowell.common.core.redis.RedisCache;
import com.jsowell.common.enums.ykc.*;
import com.jsowell.common.exception.BusinessException;
import com.jsowell.common.util.DateUtils;
import com.jsowell.common.util.StringUtils;
import com.jsowell.pile.domain.*;
import com.jsowell.pile.dto.*;
import com.jsowell.pile.service.*;
import com.jsowell.pile.transaction.service.TransactionService;
import com.jsowell.pile.vo.uniapp.PileConnectorDetailVO;
import com.jsowell.pile.vo.web.BalanceDeductionAmountVO;
import com.jsowell.pile.vo.web.BillingTemplateVO;
import com.jsowell.pile.vo.web.PileStationVO;
import com.jsowell.wxpay.service.WxAppletRemoteService;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.util.List;
import java.util.Map;

/**
 * 描述订单逻辑
 */
public abstract class AbstractOrderLogic implements InitializingBean {
    protected Logger logger = LoggerFactory.getLogger(this.getClass());

    @Value("${adapay.callback}")
    protected String ADAPAY_CALLBACK_URL;

    @Resource
    protected IOrderBasicInfoService orderBasicInfoService;

    @Autowired
    protected IPileBasicInfoService pileBasicInfoService;

    @Autowired
    protected IPileStationInfoService pileStationInfoService;

    @Autowired
    protected IPileBillingTemplateService pileBillingTemplateService;

    @Autowired
    protected IMemberBasicInfoService memberBasicInfoService;

    @Autowired
    protected MemberAdapayRecordService memberAdapayRecordService;

    @Autowired
    protected OrderPayRecordService orderPayRecordService;

    @Autowired
    protected IMemberTransactionRecordService memberTransactionRecordService;

    @Autowired
    protected AdapayService adapayService;

    @Autowired
    protected IAdapayMemberAccountService adapayMemberAccountService;

    @Autowired
    protected IPileMerchantInfoService pileMerchantInfoService;

    @Autowired
    protected TransactionService transactionService;

    @Autowired
    protected AdapayCallbackRecordService adapayCallbackRecordService;

    @Autowired
    protected IPileAuthCardService pileAuthCardService;

    @Autowired
    protected IMemberPlateNumberRelationService memberPlateNumberRelationService;

    @Autowired
    protected WxAppletRemoteService wxAppletRemoteService;

    @Autowired
    protected OrderMonitorDataService orderMonitorDataService;

    @Autowired
    protected RedisCache redisCache;

    /**
     * 生成订单
     */
    public abstract OrderBasicInfo generateOrder(GenerateOrderDTO dto) throws ParseException;

    /**
     * 支付订单
     * @param dto
     * @return
     */
    public abstract Map<String, Object> payOrder(PayOrderDTO dto);

    /**
     * 订单结算
     */
    public abstract void settleOrder(TransactionRecordsData data, OrderBasicInfo orderBasicInfo);

    /**
     * 订单退款
     */
    public abstract void refundOrder(OrderBasicInfo orderBasicInfo);

    /**
     * 余额充值
     */
    public abstract Map<String, Object> rechargeBalance(WeixinPayDTO dto);

    /**
     * 余额退款
     * @param dto
     */
    public abstract void refundBalance(ApplyRefundDTO dto);

    /**
     * 订单支付成功 支付回调
     * 支付成功后掉用这个方法
     * 1. 修改订单支付状态
     * 2. 发送启动充电指令
     */
    protected void payOrderSuccessCallback(PayOrderSuccessCallbackDTO dto) {
        orderBasicInfoService.payOrderSuccessCallback(dto);
    }

    protected List<BalanceDeductionAmountVO> calculateTheBalanceDeductionAmount(String memberId, BigDecimal amount) {
        return orderBasicInfoService.calculateTheBalanceDeductionAmount(memberId, amount);
    }

    /**
     * 处理前端传的参数
     * pileConnectorCode = pileSn + connectorCode
     *
     * @param dto
     */
    protected void analysisPileParameter(BasicPileDTO dto) {
        if (StringUtils.isBlank(dto.getPileSn()) || StringUtils.isBlank(dto.getConnectorCode())) {
            // 从pileConnectorCode解析
            String pileConnectorCode = dto.getPileConnectorCode();
            if (StringUtils.isNotEmpty(pileConnectorCode) && pileConnectorCode.length() == Constants.PILE_CONNECTOR_CODE_LENGTH) {
                dto.setPileSn(StringUtils.substring(pileConnectorCode, 0, pileConnectorCode.length() - 2));
                dto.setConnectorCode(StringUtils.substring(pileConnectorCode, pileConnectorCode.length() - 2, pileConnectorCode.length()));
            } else {
                throw new BusinessException(ReturnCodeEnum.CODE_DATA_LENGTH_ERROR);
            }
        } else {
            // 说明pileSn 和 connectorCode前端传了，那就校验一下长度
            if (dto.getPileSn().length() != Constants.PILE_SN_LENGTH || dto.getConnectorCode().length() != Constants.CONNECTOR_CODE_LENGTH) {
                throw new BusinessException(ReturnCodeEnum.CODE_DATA_LENGTH_ERROR);
            }
        }
    }

    /**
     * 校验充电桩相关的信息
     *
     * @param dto
     */
    protected void checkPileInfo(GenerateOrderDTO dto) {
        // 查询充电桩状态 是否空闲 枪口是否占用
        PileConnectorDetailVO pileConnector = pileBasicInfoService.queryPileConnectorDetail(dto.getPileSn() + dto.getConnectorCode());
        if (pileConnector == null) {
            logger.error("checkPileInfo充电枪口为空 pileSn:{}, connectorCode:{}", dto.getPileSn(), dto.getConnectorCode());
            throw new BusinessException(ReturnCodeEnum.CODE_CONNECTOR_INFO_NULL_ERROR);
        }

        // 查询站点状态
        PileStationVO stationInfo = pileStationInfoService.getStationInfo(pileConnector.getStationId());
        if (stationInfo == null || StringUtils.equals(stationInfo.getOpenFlag(), Constants.ZERO)) {
            throw new BusinessException(ReturnCodeEnum.CODE_STATION_IS_NOT_OPEN);
        }

        // 校验启动桩使用的小程序，和充电桩所属一级运营商是否一致
        String merchantIdByAppId = pileMerchantInfoService.getFirstLevelMerchantIdByAppId(dto.getAppId());
        String merchantIdByMerchantId = pileMerchantInfoService.getFirstLevelMerchantIdByMerchantId(stationInfo.getMerchantId());
        if (!StringUtils.equals(merchantIdByAppId, merchantIdByMerchantId)) {
            throw  new BusinessException("", "当前桩运营商与小程序所属运营商不一致");
        }

        // 如果是鉴权卡或者vin启动，不判断枪口状态
        if (!(StringUtils.equals(dto.getStartMode(), StartModeEnum.AUTH_CARD.getValue())
                || StringUtils.equals(dto.getStartMode(), StartModeEnum.VIN_CODE.getValue()))) {
            // 判断枪口状态
            if (!(StringUtils.equals(pileConnector.getConnectorStatus(), PileConnectorDataBaseStatusEnum.FREE.getValue())
                    || StringUtils.equals(pileConnector.getConnectorStatus(), PileConnectorDataBaseStatusEnum.OCCUPIED_NOT_CHARGED.getValue()))) {
                logger.error("checkPileInfo充电枪口状态不正确，当前状态为:{}", pileConnector.getConnectorStatus());
                throw new BusinessException(ReturnCodeEnum.CODE_PILE_CONNECTOR_STATUS_ERROR);
            }
        }
        // 查询充电桩的计费模板
        BillingTemplateVO billingTemplateVO = pileBillingTemplateService.selectBillingTemplateDetailByPileSn(dto.getPileSn());
        if (billingTemplateVO == null) {
            throw new BusinessException(ReturnCodeEnum.CODE_BILLING_TEMPLATE_NULL_ERROR);
        }
        dto.setPileConnector(pileConnector);
        dto.setBillingTemplate(billingTemplateVO);
    }



    /**
     * 返回更新后的OrderBasicInfo对象
     * 专用方法，其他地方如果要用请仔细检查
     * 【公共方法】
     */
    protected void returnUpdateOrderBasicInfo(OrderBasicInfo orderBasicInfo, TransactionRecordsData data) {
        // 订单编号
        String orderCode = orderBasicInfo.getOrderCode();
        // 消费金额就是订单总金额/交易记录传过来的消费金额
        BigDecimal orderAmount = new BigDecimal(data.getConsumptionAmount());
        // 付款金额 - 实际消费金额，如果有剩余，需要走退款操作 当使用余额支付时payAmount = principalPay + giftPay
        BigDecimal payAmount = orderBasicInfo.getPayAmount();

        // 有时候充电桩到达金额停止充电会多出一点金额，比如实际需要充50元的电，充电桩传来的消费金额为50.01元，在后台记录的时候需要舍去
        if (orderAmount.compareTo(payAmount) > 0) {
            logger.info("结算订单:【{}】充电桩传来的消费金额:【{}】大于付款金额:【{}】, 消费金额设置为付款金额相等数据", orderCode, orderAmount, payAmount);
            orderAmount = payAmount;
        }
        orderBasicInfo.setOrderAmount(orderAmount); // 订单总金额

        // 虚拟金额 指订单消费中不参与结算的部分
        BigDecimal virtualAmount = BigDecimal.ZERO;
        if (OrderPayModeEnum.PAYMENT_OF_WHITELIST.getValue().equals(orderBasicInfo.getPayMode())) {
            // 白名单支付所消费的金额，都属于虚拟金额，不参与结算对账
            virtualAmount = new BigDecimal(orderAmount.toString());
        }

        // 剩余需要退回的金额 residue
        BigDecimal residue = payAmount.subtract(orderAmount);

        // 把交易记录中的用电量，金额等信息 更新到orderBasicInfo和orderDetail
        orderBasicInfo.setVirtualAmount(virtualAmount); // 虚拟金额
        orderBasicInfo.setSettleAmount(orderAmount.subtract(virtualAmount)); // 结算金额
        orderBasicInfo.setOrderStatus(OrderStatusEnum.ORDER_COMPLETE.getValue());
        orderBasicInfo.setReason(data.getStopReasonMsg()); // 充电停止原因
        orderBasicInfo.setSettlementTime(DateUtils.getNowDate()); // 结算时间
        orderBasicInfo.setRefundAmount(residue); // 结算退款金额
    }

    /**
     * 获取更新数据后的orderDetail对象
     * 专用方法，其他地方如果要用请仔细检查
     * 【公共方法】
     * @return 查询并更新过数据的orderDetail
     */
    protected OrderDetail returnUpdateOrderDetail(OrderBasicInfo orderBasicInfo, TransactionRecordsData data) {
        String orderCode = orderBasicInfo.getOrderCode();
        BigDecimal orderAmount = orderBasicInfo.getOrderAmount();
        // 更新订单详情 查询订单详情 修改订单数据
        OrderDetail orderDetail = orderBasicInfoService.getOrderDetailByOrderCode(orderCode);
        try {
            // 总电费金额
            BigDecimal totalElectricityAmount = BigDecimal.ZERO;

            // 尖时段用电量
            String sharpUsedElectricity = data.getSharpUsedElectricity();
            if (sharpUsedElectricity != null) {
                orderDetail.setSharpUsedElectricity(new BigDecimal(sharpUsedElectricity));
                if (data.getSharpPrice() != null) {
                    orderDetail.setSharpPrice(new BigDecimal(data.getSharpPrice()));
                }
                if (data.getSharpAmount() != null) {
                    orderDetail.setSharpAmount(new BigDecimal(data.getSharpAmount()));
                }
                // 计算该时段电费
                BigDecimal multiply = orderDetail.getSharpElectricityPrice()
                        .multiply(new BigDecimal(sharpUsedElectricity))
                        .setScale(2, RoundingMode.DOWN);
                totalElectricityAmount = totalElectricityAmount.add(multiply);
            }

            // 峰时段用电量
            String peakUsedElectricity = data.getPeakUsedElectricity();
            if (peakUsedElectricity != null) {
                orderDetail.setPeakUsedElectricity(new BigDecimal(peakUsedElectricity));
                if (data.getPeakPrice() != null) {
                    orderDetail.setPeakPrice(new BigDecimal(data.getPeakPrice()));
                }
                if (data.getPeakAmount() != null) {
                    orderDetail.setPeakAmount(new BigDecimal(data.getPeakAmount()));
                }
                // 计算该时段电费
                BigDecimal multiply = orderDetail.getPeakElectricityPrice()
                        .multiply(new BigDecimal(peakUsedElectricity))
                        .setScale(2, RoundingMode.DOWN);
                totalElectricityAmount = totalElectricityAmount.add(multiply);
            }

            // 平时段用电量
            String flatUsedElectricity = data.getFlatUsedElectricity();
            if (flatUsedElectricity != null) {
                orderDetail.setFlatUsedElectricity(new BigDecimal(flatUsedElectricity));
                if (data.getFlatPrice() != null) {
                    orderDetail.setFlatPrice(new BigDecimal(data.getFlatPrice()));
                }
                if (data.getFlatAmount() != null) {
                    orderDetail.setFlatAmount(new BigDecimal(data.getFlatAmount()));
                }
                // 计算该时段电费
                BigDecimal multiply = orderDetail.getFlatElectricityPrice()
                        .multiply(new BigDecimal(flatUsedElectricity))
                        .setScale(2, RoundingMode.DOWN);
                totalElectricityAmount = totalElectricityAmount.add(multiply);
            }

            // 谷时段用电量
            String valleyUsedElectricity = data.getValleyUsedElectricity();
            if (valleyUsedElectricity != null) {
                orderDetail.setValleyUsedElectricity(new BigDecimal(valleyUsedElectricity));
                if (data.getValleyPrice() != null) {
                    orderDetail.setValleyPrice(new BigDecimal(data.getValleyPrice()));
                }
                if (data.getValleyAmount() != null) {
                    orderDetail.setValleyAmount(new BigDecimal(data.getValleyAmount()));
                }
                // 计算该时段电费
                BigDecimal multiply = orderDetail.getValleyElectricityPrice()
                        .multiply(new BigDecimal(valleyUsedElectricity))
                        .setScale(2, RoundingMode.DOWN);
                totalElectricityAmount = totalElectricityAmount.add(multiply);
            }

            // 如果算出来的电费金额大于总消费金额，则电费金额等于总消费金额
            if (totalElectricityAmount.compareTo(orderAmount) > 0) {
                totalElectricityAmount = orderAmount;
            }
            orderDetail.setTotalElectricityAmount(totalElectricityAmount);
            orderDetail.setTotalServiceAmount(orderAmount.subtract(totalElectricityAmount));
            orderDetail.setTotalUsedElectricity(new BigDecimal(data.getTotalElectricity())); // 总用电量
            orderDetail.setTotalOrderAmount(orderAmount); // 订单总金额
        } catch (Exception e) {
            logger.error("设置订单详情参数发生异常", e);
        }
        return orderDetail;
    }

    /**
     * 余额支付 计算需要退回的金额
     * 【公共方法】
     * @param principalPay 本金支付金额
     * @param giftPay 赠送金额支付的金额
     * @param orderAmount 订单消费金额
     * @return
     */
    protected Map<String, BigDecimal> calculateReturnAmount(BigDecimal principalPay, BigDecimal giftPay, BigDecimal orderAmount) {
        Map<String, BigDecimal> resultMap = Maps.newHashMap();

        // 消费金额优先使用本金
        BigDecimal returnPrincipal = null; // 退回本金金额
        BigDecimal returnGift = null; // 退回赠送金额

        // 余额支付 有3种情况
        if (principalPay != null && giftPay == null) {
            // 只有本金支付
            BigDecimal subtract = principalPay.subtract(orderAmount);
            if (subtract.compareTo(BigDecimal.ZERO) > 0) {
                returnPrincipal = subtract;
            }
        }
        if (principalPay == null && giftPay != null) {
            // 只有赠送金额支付
            BigDecimal subtract = giftPay.subtract(orderAmount);
            if (subtract.compareTo(BigDecimal.ZERO) > 0) {
                returnGift = subtract;
            }
        }
        if (principalPay != null && giftPay != null) {
            // 本金+赠送支付
            BigDecimal subtract = principalPay.subtract(orderAmount);
            if (subtract.compareTo(BigDecimal.ZERO) > 0) {
                // 本金减掉订单金额后还有剩余，那就把剩余的退回，赠送原封不动退回
                returnPrincipal = subtract;
                returnGift = giftPay;
            } else if (subtract.compareTo(BigDecimal.ZERO) == 0) {
                // 本金刚好够，那赠送金额支付的原封不动退回
                returnGift = giftPay;
            } else {
                returnGift = giftPay.subtract(subtract.negate());
            }
        }

        resultMap.put("returnPrincipal", returnPrincipal);
        resultMap.put("returnGift", returnGift);
        return resultMap;
    }

    /**
     * 计算解冻金额
     * @param orderAmount 订单消费金额
     * @param payRecordList 订单支付记录
     */
    protected List<Map<String, Object>> calculateUnfreezeAmount(BigDecimal orderAmount, List<OrderPayRecord> payRecordList) {
        List<Map<String, Object>> resultList = Lists.newArrayList();

        BigDecimal tempAmount = new BigDecimal(orderAmount.toString()); // 临时金额
        for (OrderPayRecord record : payRecordList) {
            JSONObject jsonObject = JSON.parseObject(record.getDeductionRecord());
            String paymentId = jsonObject.getString("paymentId");
            BigDecimal payAmount = record.getPayAmount(); // 此交易单支付的金额
            // 该笔支付扣除金额
            BigDecimal deductionAmount;
            // 该笔支付解冻金额
            BigDecimal unfreezeAmount = null;
            // 临时消费金额 = 临时消费金额 - 该笔交易的剩余金额
            tempAmount = tempAmount.subtract(payAmount);
            if (tempAmount.compareTo(BigDecimal.ZERO) >= 0) {
                // 计算以后，大于等于0，说明这笔支付剩余金额需要扣完，还要继续扣下一笔
                deductionAmount = payAmount;
                unfreezeAmount = payAmount.subtract(deductionAmount); // 支付金额 - 扣除金额 = 需要退回的金额
                Map<String, Object> map = Maps.newHashMap();
                map.put("paymentId", paymentId);
                map.put("unfreezeAmount", unfreezeAmount);
                resultList.add(map);
            } else {
                // 如果小于0，则说明该笔交易的剩余金额用不完，扣除金额等于临时消费金额，并结束循环
                deductionAmount = payAmount.add(tempAmount); // 该笔交易的剩余金额加上一个负数临时消费金额，就是该笔交易扣除金额
                unfreezeAmount = payAmount.subtract(deductionAmount); // 支付金额 - 扣除金额 = 需要退回的金额
                Map<String, Object> map = Maps.newHashMap();
                map.put("paymentId", paymentId);
                map.put("unfreezeAmount", unfreezeAmount);
                resultList.add(map);
                break;
            }
        }
        return resultList;
    }

    /**
     * 从redis中取出实时记录保存到表中j
     * 当订单完成的时候调用
     * 【公共方法】
     */
    protected void realTimeMonitorDataRedis2DB(String transactionCode, String orderCode) {
        try {
            if (StringUtils.isBlank(transactionCode) || StringUtils.isBlank(orderCode)) {
                return;
            }
            // 校验有没有保存过
            OrderMonitorData orderMonitorData = orderMonitorDataService.selectByOrderCode(orderCode);
            if (orderMonitorData != null) {
                return;
            }
            List<RealTimeMonitorData> chargingRealTimeData = orderBasicInfoService.getChargingRealTimeData(transactionCode);
            if (CollectionUtils.isEmpty(chargingRealTimeData)) {
                return;
            }
            List<RealTimeMonitorData> list = Lists.newArrayList();
            for (RealTimeMonitorData data : chargingRealTimeData) {
                RealTimeMonitorData build = RealTimeMonitorData.builder()
                        .outputCurrent(data.getOutputCurrent())
                        .outputCurrent(data.getOutputCurrent()) // 电流
                        .outputVoltage(data.getOutputVoltage()) // 电压
                        .outputPower(data.getOutputPower())  // 功率
                        .SOC(data.getSOC()) // soc
                        .dateTime(data.getDateTime())  // 时间
                        .batteryMaxTemperature(data.getBatteryMaxTemperature())
                        .chargingAmount(data.getChargingAmount())
                        .chargingDegree(data.getChargingDegree())
                        .sumChargingTime(data.getSumChargingTime())
                        .timeRemaining(data.getTimeRemaining())
                        .gunLineTemperature(data.getGunLineTemperature())
                        .build();
                list.add(build);
            }

            OrderMonitorData record = new OrderMonitorData();
            record.setOrderCode(orderCode);
            record.setTransactionCode(transactionCode);
            record.setMonitorData(JSONObject.toJSONString(list));
            int insert = orderMonitorDataService.insertSelective(record);
            if (insert > 0) {
                // 删除redis中缓存
                String pileConnectorCode = transactionCode.substring(0, 16);
                String redisKey = CacheConstants.PILE_REAL_TIME_MONITOR_DATA + pileConnectorCode + "_" + transactionCode;
                redisCache.deleteObject(redisKey);
            }
        } catch (Exception e) {
            logger.info("redis中取出实时记录保存到表发生异常", e);
        }
    }

    /**
     * 卡状态解锁
     * 【公共方法】
     */
    protected void cardStatusUnlocked(String logicCard) {
        try {
            // 根据物理卡号查出当前为锁定状态的卡
            PileAuthCard cardInfo = pileAuthCardService.selectSomeStatusCardInfo(CardStatusEnum.START_LOCK.getCode(), logicCard);
            if (cardInfo != null) {
                // 将此卡状态改为正常
                cardInfo.setStatus(CardStatusEnum.NORMAL.getCode());
                pileAuthCardService.updatePileAuthCard(cardInfo);
            }
        } catch (Exception e) {
            logger.error("解锁卡状态 error,", e);
        }
    }

    /**
     * 解锁vin状态
     * 【公共方法】
     * @param vinCode
     */
    protected void vinStatusUnlocked(String vinCode) {
        try {
            MemberPlateNumberRelation plateInfo = memberPlateNumberRelationService.getMemberPlateInfoByVinCode(vinCode);
            if (plateInfo != null && (StringUtils.equals(plateInfo.getVinStatus(), "2"))) {
                plateInfo.setVinStatus("1");
                memberPlateNumberRelationService.updateMemberPlateNumberRelation(plateInfo);
            }
        } catch (Exception e) {
            logger.error("解锁vin状态 error,", e);
        }
    }
}
