package com.kym.service.miniapp.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.kym.common.config.EnPlusConfig;
import com.kym.common.constant.ResponseEnum;
import com.kym.common.exception.BusinessException;
import com.kym.common.utils.OrderUtils;
import com.kym.entity.enplus.response.EnBusinessPolicy;
import com.kym.entity.miniapp.ChargeOrder;
import com.kym.service.admin.EquipmentRelationService;
import com.kym.service.enplus.EnPlusService;
import com.kym.service.miniapp.AccountService;
import com.kym.service.miniapp.ChargeOrderService;
import com.kym.service.miniapp.ChargeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.Map;

/**
 * @author skyline
 * @description 充电
 * @date 2023-07-29 14:24
 */
@Service
@DS("db-miniapp")
public class ChargeServiceImpl implements ChargeService {
    private static final Logger LOGGER = LoggerFactory.getLogger(ChargeServiceImpl.class);

    private final EquipmentRelationService equipmentRelationService;

    private final ChargeOrderService chargeOrderService;

    private final AccountService accountService;

    private final EnPlusService enPlusService;

    private final EnPlusConfig enPlusConfig;

    public ChargeServiceImpl(EquipmentRelationService equipmentRelationService, ChargeOrderService chargeOrderService, AccountService accountService, EnPlusService enPlusService, EnPlusConfig enPlusConfig) {
        this.equipmentRelationService = equipmentRelationService;
        this.chargeOrderService = chargeOrderService;
        this.accountService = accountService;
        this.enPlusService = enPlusService;
        this.enPlusConfig = enPlusConfig;
    }

    /**
     * 启动充电
     *
     * @param connectorId
     * @return
     */
    @Override
    public Map<String, String> queryStartCharge(String connectorId) {
        var userId = StpUtil.getLoginIdAsLong();
        var map = getConnectorIdAndStationId(connectorId);
        connectorId = map.get("connectorId");
        var stationId = map.get("stationId");
        LOGGER.info("用户：{},设备：{}请求启动充电", userId, connectorId);
        // 二维码文本
        var qrCode = "";
        // 当前设备是是否有正在进行中的订单
        var chargeOrder = chargeOrderService.getChargingOrderByUserId(userId);
        if (chargeOrder != null) {
            LOGGER.error("用户：{}存在进行中的订单：{}", userId, chargeOrder.getId());
            throw new BusinessException(ResponseEnum.ORDER_IN_PROGRESS);
        }
        // 查询用户余额
        var account = accountService.getAccountByUserId(userId);
        if (account.getBalance() <= 200) {
            LOGGER.error("用户：{}余额不足2元，余额：{}", userId, account.getBalance());
            throw new BusinessException(ResponseEnum.INSUFFICIENT_USER_BALANCE);
        }
        // 传递给EN+的余额要小于实际余额，防止订单超扣的情况，这里少传0.5元
        var amount = account.getBalance() - 50;

        // 充电订单号/设备认证号
        String startChargeSeq = OrderUtils.getOrderNo(enPlusConfig.getOperatorId());

        // 请求设备认证
        var equipAuth = enPlusService.queryEquipAuth(connectorId, startChargeSeq);

        if (equipAuth.containsKey("SuccStat") && equipAuth.getIntValue("SuccStat") == 0) {
            // TODO 查询业务策略信息（计费信息）,目前计费在EN+完成，后续自主计费需要开发
            // 启动充电
            var startCharge = enPlusService.queryStartCharge(startChargeSeq, connectorId, qrCode, amount);
            if (startCharge.containsKey("SuccStat") && startCharge.getIntValue("SuccStat") == 0) {
                // 启动成功，生成充电订单
                var order = new ChargeOrder();
                order.setUserId(userId);
                order.setStationId(stationId);
                order.setStartChargeSeq(startChargeSeq);
                order.setConnectorId(connectorId);
                order.setOrderStatus(ChargeOrder.ORDER_STATUS_未知);
                order.setChargeStatus(startCharge.getIntValue("StartChargeSeqStat"));
                chargeOrderService.save(order);
                return Map.of("startChargeSeq", startChargeSeq);
            } else {
                // 启动充电失败
                LOGGER.error("设备启动充电失败：{}", startCharge);
                switch (equipAuth.getIntValue("FailReason")) {
                    case 1 -> // 设备不存在
                            throw new BusinessException(ResponseEnum.EN_PLUS_EQUIP_NOT_EXIST);
                    case 2 -> // 设备离线
                            throw new BusinessException(ResponseEnum.EN_PLUS_EQUIP_OFFLINE);
                    case 3 -> // 存在未完成的订单
                            throw new BusinessException(ResponseEnum.EN_PLUS_EQUIP_EXIST_ORDER_UNFINISHED);
                    case 4 -> // 此订单为停车中
                            throw new BusinessException(ResponseEnum.EN_PLUS_ORDER_IS_PARKING);
                    case 5 -> // 此订单已关闭
                            throw new BusinessException(ResponseEnum.EN_PLUS_ORDER_CLOSED);
                    case 6 -> // 设备未插枪
                            throw new BusinessException(ResponseEnum.EN_PLUS_EQUIP_NOT_CONNECTED);
                    case 7 -> // 预充金额存在且小于1元
                            throw new BusinessException(ResponseEnum.INSUFFICIENT_USER_BALANCE);
                }
                throw new BusinessException(ResponseEnum.EN_PLUS_EQUIP_START_FAIL);
            }
        } else {
            // 设备认证失败
            LOGGER.error("设备认证失败：{}", equipAuth);
            switch (equipAuth.getIntValue("FailReason")) {
                case 1 -> // 设备未插枪
                        throw new BusinessException(ResponseEnum.EN_PLUS_EQUIP_NOT_CONNECTED);
                case 2 -> // 设备检测失败
                        throw new BusinessException(ResponseEnum.EN_PLUS_EQUIP_CHECK_FAIL);
            }
            throw new BusinessException(ResponseEnum.EN_PLUS_EQUIP_AUTH_FAIL);
        }
    }


    /**
     * 请求EN+设备充电状态
     *
     * @return
     */
    @Override
    public ChargeOrder queryEquipChargeStatus() {
        var userId = StpUtil.getLoginIdAsLong();
        var chargeOrder = chargeOrderService.getChargingOrderByUserId(userId);
        if (chargeOrder != null) {
            // 查询充电
            var startChargeSeq = chargeOrder.getStartChargeSeq();
            var data = enPlusService.queryEquipChargeStatus(startChargeSeq);

            // 更新订单信息
            chargeOrder.setSoc(data.getDoubleValue("Soc"));
            chargeOrder.setTotalPower(data.getDoubleValue("TotalPower"));
            chargeOrder.setTotalMoney((int) ((data.getDouble("TotalMoney") * 100)));
            chargeOrder.setElecMoney((int) ((data.getDouble("ElecMoney") * 100)));
            chargeOrder.setServiceMoney((int) ((data.getDouble("SeviceMoney") * 100))); // 这里文档service单词错误，按文档填写
            chargeOrder.setSumPeriod(data.getIntValue("SumPeriod"));
            chargeOrder.setChargeDetail(data.getString("ChargeDetails"));
            chargeOrder.setChargeStatus(data.getIntValue("StartChargeSeqStat"));
            chargeOrderService.updateById(chargeOrder);
            return chargeOrder;
        } else {
            LOGGER.debug("用户：{}无进行中的订单", userId);
            return null;
        }
    }

    public Map<String, String> getConnectorIdAndStationId(String connectorId) {
        var stationId = "";
        if (connectorId.length() == 17) {
            var equipmentRelation = equipmentRelationService.getByEquipmentId(connectorId.substring(0, 16));
            stationId = equipmentRelation.getStationId();
        } else if (connectorId.length() == 6) {
            // 查询EN+设备SN
            var equipmentRelation = equipmentRelationService.getByShortId(connectorId);
            connectorId = equipmentRelation.getEquipmentId();
            stationId = equipmentRelation.getStationId();
            if (connectorId.length() == 16) {
                // 如果是16位，末尾补1作为单枪枪号
                connectorId = connectorId.concat("1");
            }
        } else {
            // 充电桩编号错误
            LOGGER.error("设备编码错误：{}", connectorId);
            throw new BusinessException(ResponseEnum.EQUIP_CONNECTOR_ID_ERROR);
        }
        return Map.of("connectorId", connectorId, "stationId", stationId);
    }

    public String getConnectorId(String connectorId) {
        if (connectorId.length() == 17) {
            return connectorId;
        } else if (connectorId.length() == 6) {
            // 查询EN+设备SN
            var equipmentRelation = equipmentRelationService.getByShortId(connectorId);
            connectorId = equipmentRelation.getEquipmentId();
            if (connectorId.length() == 16) {
                // 如果是16位，末尾补1作为单枪枪号
                connectorId = connectorId.concat("1");
            }
        } else {
            // 充电桩编号错误
            LOGGER.error("设备编码错误：{}", connectorId);
            throw new BusinessException(ResponseEnum.EQUIP_CONNECTOR_ID_ERROR);
        }
        return connectorId;
    }


    /**
     * 请求停止充电
     *
     * @param connectorId
     */
    @Override
    public void queryStopCharge(String connectorId) {
        var userId = StpUtil.getLoginIdAsLong();
        connectorId = getConnectorId(connectorId);
        LOGGER.info("用户：{},设备：{}请求停止充电", userId, connectorId);
        // 查询充电订单
        var chargeOrder = chargeOrderService.lambdaQuery()
                .eq(ChargeOrder::getUserId, userId)
                .eq(ChargeOrder::getConnectorId, connectorId)
                .eq(ChargeOrder::getChargeStatus, ChargeOrder.CHARGE_STATUS_充电中)
                .one();
        if (chargeOrder == null) {
            // 记录失败原因
            LOGGER.error("用户：{}请求停止充电异常，设备：{}无进行中的订单", userId, connectorId);
            throw new BusinessException(ResponseEnum.NO_ORDER_IN_PROGRESS);
        }
        var data = enPlusService.queryStopCharge(chargeOrder.getStartChargeSeq(), connectorId);
        if (data.containsKey("SuccStat") && data.getIntValue("SuccStat") != 0) {
            // 记录失败原因
            LOGGER.error("订单：{}停止充电失败，返回结果:{}", chargeOrder.getStartChargeSeq(), data);
            throw new BusinessException(ResponseEnum.EN_PLUS_EQUIP_STOP_FAIL);
        }
        // 这里不更新订单状态，等待EN+推送停止充电结果信息再处理

    }


    /**
     * 请求设备计费策略
     *
     * @param connectorId
     * @return
     */
    @Override
    public EnBusinessPolicy queryEquipBusinessPolicy(String connectorId) {
        var equipBizSeq = OrderUtils.getOrderNo(enPlusConfig.getOperatorId());
        var data = enPlusService.queryEquipBusinessPolicy(equipBizSeq, connectorId);
        return data.toJavaObject(EnBusinessPolicy.class);
    }

}
