package cn.newfeifan.mall.module.distri.service.integral;

import cn.newfeifan.mall.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.newfeifan.mall.module.distri.constant.DistriConstants;
import cn.newfeifan.mall.module.distri.controller.app.integralchangelog.vo.IntegralChangeLogSaveReqVO;
import cn.newfeifan.mall.module.distri.controller.app.ptprofitlog.vo.PtProfitLogSaveReqVO;
import cn.newfeifan.mall.module.distri.dal.dataobject.duser.DuserDO;
import cn.newfeifan.mall.module.distri.dal.dataobject.orderpercentage.OrderPercentageDO;
import cn.newfeifan.mall.module.distri.dal.dataobject.ptprofit.PtProfitDO;
import cn.newfeifan.mall.module.distri.dal.mysql.ptprofit.PtProfitMapper;
import cn.newfeifan.mall.module.distri.enums.CaclEnum;
import cn.newfeifan.mall.module.distri.enums.IntegralEnum;
import cn.newfeifan.mall.module.distri.enums.IntegralTypeEnum;
import cn.newfeifan.mall.module.distri.enums.ProfitTypeEnum;
import cn.newfeifan.mall.module.distri.service.integralchangelog.IntegralChangeLogService;
import cn.newfeifan.mall.module.distri.service.duser.DuserService;
import cn.newfeifan.mall.module.distri.service.orderpercentage.OrderPercentageService;
import cn.newfeifan.mall.module.distri.service.ptprofitlog.PtProfitLogService;
import cn.newfeifan.mall.module.member.dal.dataobject.user.MemberUserDO;
import cn.newfeifan.mall.module.member.service.user.MemberUserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.validation.annotation.Validated;

import java.util.*;

import cn.newfeifan.mall.module.distri.controller.app.integral.vo.*;
import cn.newfeifan.mall.module.distri.dal.dataobject.integral.IntegralDO;
import cn.newfeifan.mall.framework.common.util.object.BeanUtils;

import cn.newfeifan.mall.module.distri.dal.mysql.integral.IntegralMapper;

import static cn.newfeifan.mall.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.newfeifan.mall.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.newfeifan.mall.module.distri.enums.ErrorCodeConstants.*;
import static cn.newfeifan.mall.module.member.enums.ErrorCodeConstants.USER_NOT_PAY_PASSWORD;
import static cn.newfeifan.mall.module.member.enums.ErrorCodeConstants.USER_PAY_PASS_WORD_ERROR;

/**
 * 推荐用户积分 Service 实现类
 *
 * @author 非繁人
 */
@Service
@Validated
public class IntegralServiceImpl implements IntegralService {

    @Resource
    private IntegralMapper integralMapper;
    @Resource
    private PtProfitMapper ptProfitMapper;
    @Resource
    private PtProfitLogService ptProfitLogService;
    @Resource
    private IntegralChangeLogService integralChangeLogService;
    @Resource
    private OrderPercentageService orderPercentageService;
    @Resource
    @Lazy
    private DuserService duserService;
    @Resource
    private MemberUserService memberUserService;
    @Resource
    private PasswordEncoder passwordEncoder;

    @Override
    public Long createIntegral(IntegralSaveReqVO createReqVO) {
        // 插入
        IntegralDO integral = BeanUtils.toBean(createReqVO, IntegralDO.class);
        integralMapper.insert(integral);
        // 返回
        return integral.getId();
    }

    @Override
    public void updateIntegral(IntegralSaveReqVO updateReqVO) {
        // 校验存在
        validateIntegralExists(updateReqVO.getId());
        // 更新
        IntegralDO updateObj = BeanUtils.toBean(updateReqVO, IntegralDO.class);
        integralMapper.updateById(updateObj);
    }

    private void validateIntegralExists(Long id) {
        if (integralMapper.selectById(id) == null) {
            throw exception(INTEGRAL_NOT_EXISTS);
        }
    }

    @Override
    public IntegralDO getIntegral(Long id) {
        return integralMapper.selectOne(new LambdaQueryWrapperX<IntegralDO>().eq(IntegralDO::getUserId, id));
    }

    @Override
    public IntegralDO selectByUser(Long descendant) {
        return integralMapper.selectOne(new LambdaQueryWrapper<IntegralDO>().eq(IntegralDO::getUserId, descendant));
    }

    private void verifyUserIntegral(Long userIntegral, Long useIntegral) {
        if (userIntegral < useIntegral) {
            throw exception(USER_INTEGRAL_LESS_TRANSITION_INTEGRAL);
        }
    }

    /**
     * add by Ben
     * 修改用户积分
     *
     * @param generateUserId 要修改积分的用户
     * @param userId         要修改积分的用户
     * @param integralEnum       导致积分变化的事件类型枚举
     * @param amount         用户可用积分变化金额，可为负数
     *  返回是否执行成功。执行失败，通常是就是用户积分不足，没积分抵扣
     */
    @Override
    public void updateUserIntegral(Long generateUserId,//导致产生积分变动的用户ID
                                   Long userId, IntegralEnum integralEnum, Long amount,
                                   Long tradeOrderId,
                                   String OrderNum,
                                   Integer integralType,
                                   Long highQuota
    ) {
        if (amount == null || amount == 0L) return;

        // 用户钱包
        IntegralDO integralDO = integralMapper.selectOne(new LambdaQueryWrapperX<IntegralDO>().eq(IntegralDO::getUserId, userId));
        Long afterIntegralPoints = 0L;

        // 溢出额度
        Long ancestorQuotaAmount = null;
        Long getHighQuota = 0L;     // 实得直推奖奖

        // 校验用户积分是否足够
        if (integralType.equals(IntegralTypeEnum.RED_INTEGRAL.getType())) {
            verifyUserIntegral(integralDO.getRedCurrentQuota(), amount);
            getHighQuota = -amount;

            // 修改用户积分
            integralDO.setRedCurrentQuota(integralDO.getRedCurrentQuota() - amount);
            afterIntegralPoints = integralDO.getRedCurrentQuota();

            if (highQuota != null) {
                // 增加峰值
                integralDO.setHighQuota(integralDO.getHighQuota() + highQuota);     //剩余峰值
                integralDO.setHighQuotaTotal(integralDO.getHighQuotaTotal() + highQuota);      //累计峰值
            }
        } else if (integralType.equals(IntegralTypeEnum.GREEN_INTEGRAL.getType())) {
            verifyUserIntegral(integralDO.getGreenCurrentQuota(), amount);
            getHighQuota = -amount;

            // 修改用户积分
            integralDO.setGreenCurrentQuota(integralDO.getGreenCurrentQuota() - amount);
            afterIntegralPoints = integralDO.getGreenCurrentQuota();
        } else if (integralType.equals(IntegralTypeEnum.YELLOW_INTEGRAL.getType())) {
            // 峰值小于获得额度
            if (integralDO.getHighQuota() < amount) {
                ancestorQuotaAmount = amount - integralDO.getHighQuota();
                getHighQuota = integralDO.getHighQuota();
                highQuota = -integralDO.getHighQuota();

                integralDO.setYellowCurrentQuota(integralDO.getYellowCurrentQuota() + integralDO.getHighQuota());
                integralDO.setHighQuota(0L);
            } else {
                getHighQuota = amount;
                highQuota = -amount;
                integralDO.setHighQuota(integralDO.getHighQuota() - amount);
                integralDO.setYellowCurrentQuota(integralDO.getYellowCurrentQuota() + amount);
            }
            afterIntegralPoints = integralDO.getYellowCurrentQuota();
        }

        integralMapper.updateById(integralDO);

        amount = integralType.equals(IntegralTypeEnum.RED_INTEGRAL.getType()) ||
                integralType.equals(IntegralTypeEnum.GREEN_INTEGRAL.getType()) ? -amount : amount;

        // 记录积分变动日志
        IntegralChangeLogSaveReqVO changeLog = IntegralChangeLogSaveReqVO.builder()
                .userId(userId)
                .integralPoints(amount)
                .afterIntegralPoints(afterIntegralPoints)
                .practicalIntegralPoints(getHighQuota)
                .generateUserId(generateUserId)
                .orderId(tradeOrderId)
                .orderNo(OrderNum)
                .integralStatus(integralEnum.getType())
                .maxAvailablePointsAmount(highQuota)
                .afterMaxAvailablePointsAmount(integralDO.getHighQuota())
                .ancestorQuotaAmount(ancestorQuotaAmount)
                .integralType(integralType)
                .build();
        integralChangeLogService.createIntegralChangeLog(changeLog);
    }

    @Override
    public void updateSHUserIntegral(Long orderId, String orderNum, Long amount, Long userId, IntegralEnum integralEnum, Long generateUserId) {
        // 增加商户用户黄积分
        IntegralDO shIntegral = integralMapper.selectOne(new LambdaQueryWrapperX<IntegralDO>().eq(IntegralDO::getUserId, userId));
        shIntegral.setYellowCurrentQuota(shIntegral.getYellowCurrentQuota() + amount);
        integralMapper.updateById(shIntegral);

        // 记录积分变动日志
        IntegralChangeLogSaveReqVO changeLog = IntegralChangeLogSaveReqVO.builder()
                .userId(userId)
                .integralPoints(amount)
                .afterIntegralPoints(shIntegral.getYellowCurrentQuota())
                .practicalIntegralPoints(amount)
                .generateUserId(generateUserId)
                .orderId(orderId)
                .orderNo(orderNum)
                .integralStatus(integralEnum.getType())
                .integralType(IntegralTypeEnum.YELLOW_INTEGRAL.getType())
                .build();
        integralChangeLogService.createIntegralChangeLog(changeLog);
    }

    @Override
    public void updateUserIntegralByBonus(Long generateUserId, Long userId, IntegralEnum integralEnum, Long amount, Long orderId, String orderNo) {
        if (amount == null || amount == 0L) return;

        // 用户钱包
        IntegralDO integralDO = integralMapper.selectOne(new LambdaQueryWrapperX<IntegralDO>().eq(IntegralDO::getUserId, userId));
        // 参数比例
        OrderPercentageDO orderPercentageDO = orderPercentageService.queryStatus();

        // 记录积分变动日志
        IntegralChangeLogSaveReqVO yellowChangeLog = IntegralChangeLogSaveReqVO.builder()
                .integralType(IntegralTypeEnum.YELLOW_INTEGRAL.getType())
                .userId(userId)
                .generateUserId(generateUserId)
                .integralStatus(integralEnum.getType())
                .orderId(orderId)
                .orderNo(orderNo)
                .build();
        IntegralChangeLogSaveReqVO greenChangeLog = IntegralChangeLogSaveReqVO.builder()
                .integralType(IntegralTypeEnum.GREEN_INTEGRAL.getType())
                .userId(userId)
                .generateUserId(generateUserId)
                .integralStatus(integralEnum.getType())
                .orderId(orderId)
                .orderNo(orderNo)
                .build();

        String bonusYellowIntegral = orderPercentageDO.getBonusYellowIntegral();
        String bonusGreenIntegral = orderPercentageDO.getBonusGreenIntegral();

        Long ancestorYellowQuotaAmount = 0L;        // 溢出黄积分额度
        Long ancestorGreenQuotaAmount = 0L;        // 溢出绿积分额度
        Long getYellowHighQuota;     // 实得直推奖黄积分
        Long getGreenHighQuota;     // 实得直推奖绿积分
        if (integralDO.getHighQuota() < amount) {
            ancestorYellowQuotaAmount = calcIntegral((amount - integralDO.getHighQuota()), bonusYellowIntegral);
            getYellowHighQuota = calcIntegral(integralDO.getHighQuota(), bonusYellowIntegral);

            ancestorGreenQuotaAmount = calcIntegral((amount - integralDO.getHighQuota()), bonusGreenIntegral);
            getGreenHighQuota = calcIntegral(integralDO.getHighQuota(), bonusGreenIntegral);
        } else {
            getYellowHighQuota = calcIntegral(amount, bonusYellowIntegral);

            getGreenHighQuota = calcIntegral(amount, bonusGreenIntegral);
        }

        integralDO.setYellowCurrentQuota(integralDO.getYellowCurrentQuota() + getYellowHighQuota);
        integralDO.setGreenCurrentQuota(integralDO.getGreenCurrentQuota() + getGreenHighQuota);
        integralDO.setHighQuota(integralDO.getHighQuota() - getYellowHighQuota - getGreenHighQuota);
        integralDO.setAncestorQuota(integralDO.getAncestorQuota() + getYellowHighQuota + getGreenHighQuota);

        yellowChangeLog
                .setIntegralPoints(getYellowHighQuota)
                .setPracticalIntegralPoints(getYellowHighQuota)
                .setAfterIntegralPoints(integralDO.getYellowCurrentQuota())
                .setAncestorQuotaAmount(ancestorYellowQuotaAmount)
                .setMaxAvailablePointsAmount(-getYellowHighQuota)
                .setAfterMaxAvailablePointsAmount(integralDO.getHighQuota() + getGreenHighQuota);

        greenChangeLog.
                setIntegralPoints(getGreenHighQuota)
                .setPracticalIntegralPoints(getGreenHighQuota)
                .setAfterIntegralPoints(integralDO.getGreenCurrentQuota())
                .setAncestorQuotaAmount(ancestorGreenQuotaAmount)
                .setMaxAvailablePointsAmount(-getGreenHighQuota)
                .setAfterMaxAvailablePointsAmount(integralDO.getHighQuota() + getYellowHighQuota);

        // 修改用户积分
        integralMapper.updateById(integralDO);

        // 插入积分变动日志
        integralChangeLogService.createIntegralChangeLogs(Arrays.asList(yellowChangeLog, greenChangeLog));

        // 用户获得直推奖后要扣除平台的余额
        if (getYellowHighQuota + getGreenHighQuota > 0) {
            PtProfitDO ptProfitDO = ptProfitMapper.selectList().get(0);
            ptProfitDO.setPtGrossAdd(ptProfitDO.getPtGrossAdd() - getYellowHighQuota - getGreenHighQuota);
            ptProfitDO.setPtTotalAdd(ptProfitDO.getPtTotalAdd() - getYellowHighQuota - getGreenHighQuota);

            PtProfitLogSaveReqVO ptProfitLog = PtProfitLogSaveReqVO.builder()
                    .profitStatus(integralEnum.getType())
                    .profitType(ProfitTypeEnum.PT_PROFIT.getType())
                    .amount(-(getYellowHighQuota + getGreenHighQuota))
                    .afterAmount(ptProfitDO.getPtGrossAdd())
                    .orderId(orderId)
                    .orderNo(orderNo)
                    .generateUserId(userId)
                    .build();

            ptProfitMapper.updateById(ptProfitDO);
            ptProfitLogService.createPtProfitLog(ptProfitLog);
        }
    }

    /**
     * 计算积分
     * @param amount 值
     * @param bonusIntegral 比例
     * @return 值
     */
    private Long calcIntegral(Long amount, String bonusIntegral) {
        return (long) (amount * Double.parseDouble(bonusIntegral));
    }

    @Override
    public void updateUserIntegral(Long userId, CaclEnum caclEnum, Long payIntegral, Long tradeOrderId, String orderNum) {
        //退还订单支付过期积分
        IntegralDO integralDO = integralMapper.selectOne(new LambdaQueryWrapper<IntegralDO>().eq(IntegralDO::getUserId, userId));
        integralDO.setRedCurrentQuota(integralDO.getRedCurrentQuota() + payIntegral);

        integralMapper.updateById(integralDO);

        ptProfitLogService.addMessage(userId, userId, caclEnum, payIntegral, integralDO.getRedCurrentQuota(), 0L, 0L, null, tradeOrderId, orderNum);
    }

    @Override
    public Boolean quotaTransition(IntegralTransitionRespVO respVO) {
        Long userId = getLoginUserId();
        DuserDO duser = duserService.getDuserByUser(userId);

        // 获取用户信息并检查支付密码
        MemberUserDO user = memberUserService.getUser(userId);
        if (user.getPayPassword() == null || user.getPayPassword().isEmpty()) {
            throw exception(USER_NOT_PAY_PASSWORD);
        }

        // 支付密码错误
        if (!passwordEncoder.matches(respVO.getPayPassword(), user.getPayPassword())) {
            throw exception(USER_PAY_PASS_WORD_ERROR);
        }

        // 转换倍率
        OrderPercentageDO orderPercentageDO = orderPercentageService.queryStatus();

        // 输入的佣金要放大才能与数据库中匹配
        respVO.setQuota(respVO.getQuota() * DistriConstants.INTEGRAL_PAGE2DB);

        // 变动积分
        Long practicalConsumptionPoints = 0L;
        // 变动积分剩余
        Long afterIntegralPoints = 0L;
        // 增加积分剩余
        Long afterAddIntegralPoints = 0L;
        IntegralDO integralDO = integralMapper.selectOne(IntegralDO::getUserId, userId);
        Integer integralStatus = 0;

        // 确定转账类型
        switch (respVO.getIntegralType()) {
            case 1:     //红转绿
                // 身份校验
                if (duser.getCategoryId().equals(1L) || duser.getCategoryId().equals(2L))
                    throw exception(USER_IS_NOT_AGENT);

                // 存量校验
                verifyIntegral(integralDO.getRedCurrentQuota(), respVO.getQuota());

                integralDO.setRedCurrentQuota(integralDO.getRedCurrentQuota() - respVO.getQuota());
                practicalConsumptionPoints = (long) (respVO.getQuota() * Double.parseDouble(orderPercentageDO.getAgentRedTransitionGreen()));
                integralDO.setGreenCurrentQuota(integralDO.getGreenCurrentQuota() + practicalConsumptionPoints);
                afterIntegralPoints = integralDO.getRedCurrentQuota();
                afterAddIntegralPoints = integralDO.getGreenCurrentQuota();

                integralStatus = IntegralEnum.RED_INTEGRAL_TRANSITION_GREEN_INTEGRAL.getType();
                break;
            case 2:     //黄转红
                verifyIntegral(integralDO.getYellowCurrentQuota(), respVO.getQuota());

                integralDO.setYellowCurrentQuota(integralDO.getYellowCurrentQuota() - respVO.getQuota());
                if (duser.getCategoryId().equals(1L) || duser.getCategoryId().equals(2L)) {     //普通用户和会员
                    practicalConsumptionPoints = (long) (respVO.getQuota() * Double.parseDouble(orderPercentageDO.getMemberYellowTransitionRed()));
                } else {        // 代理商
                    practicalConsumptionPoints = (long) (respVO.getQuota() * Double.parseDouble(orderPercentageDO.getAgentYellowTransitionRed()));
                }
                integralDO.setRedCurrentQuota(integralDO.getRedCurrentQuota() + practicalConsumptionPoints);
                afterIntegralPoints = integralDO.getYellowCurrentQuota();
                afterAddIntegralPoints = integralDO.getRedCurrentQuota();

                integralStatus = IntegralEnum.YELLOW_INTEGRAL_TRANSITION_RED_INTEGRAL.getType();
                break;
            // 目前没有绿积分转换
        }

        integralMapper.updateById(integralDO);

        // 记录扣减变动日志
        integralChangeLogService.createIntegralChangeLog(
                IntegralChangeLogSaveReqVO.builder()
                        .userId(userId)
                        .integralStatus(integralStatus)
                        .integralPoints(-respVO.getQuota())
                        .practicalIntegralPoints(-respVO.getQuota())
                        .afterIntegralPoints(afterIntegralPoints)
                        .integralType(respVO.getIntegralType())
                        .generateUserId(userId)
                        .build());

        // 记录增加变动日志
        integralChangeLogService.createIntegralChangeLog(
                IntegralChangeLogSaveReqVO.builder()
                        .userId(userId)
                        .integralStatus(integralStatus)
                        .integralPoints(respVO.getQuota())
                        .practicalIntegralPoints(practicalConsumptionPoints)
                        .afterIntegralPoints(afterAddIntegralPoints)
                        .integralType(Objects.equals(respVO.getIntegralType(), IntegralTypeEnum.RED_INTEGRAL.getType()) ?
                                IntegralTypeEnum.GREEN_INTEGRAL.getType() : IntegralTypeEnum.RED_INTEGRAL.getType())
                        .generateUserId(userId)
                        .build());

        return true;
    }

    /**
     * 校验用户积分是否充足
     *
     * @param userIntegral       用户积分
     * @param transitionIntegral 转换积分
     */
    private void verifyIntegral(Long userIntegral, Long transitionIntegral) {
        if (userIntegral < transitionIntegral) {
            throw exception(USER_INTEGRAL_LESS_TRANSITION_INTEGRAL);
        }
    }
}