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

import cn.newfeifan.mall.framework.common.exception.ErrorCode;
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.integraltransferlog.vo.IntegralTransferLogPageReqVO;
import cn.newfeifan.mall.module.distri.controller.app.integraltransferlog.vo.IntegralTransferLogSaveReqVO;
import cn.newfeifan.mall.module.distri.controller.app.integral.vo.IntegralSaveReqVO;
import cn.newfeifan.mall.module.distri.dal.dataobject.integral.IntegralDO;
import cn.newfeifan.mall.module.distri.enums.IntegralEnum;
import cn.newfeifan.mall.module.distri.service.integralchangelog.IntegralChangeLogService;
import cn.newfeifan.mall.module.distri.service.integral.IntegralService;
import cn.newfeifan.mall.module.distri.utils.ReflectionUtils;
import cn.newfeifan.mall.module.member.dal.dataobject.user.MemberUserDO;
import cn.newfeifan.mall.module.member.service.user.MemberUserService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import cn.newfeifan.mall.module.distri.dal.dataobject.integraltransferlog.IntegralTransferLogDO;
import cn.newfeifan.mall.framework.common.pojo.PageResult;
import cn.newfeifan.mall.framework.common.util.object.BeanUtils;

import cn.newfeifan.mall.module.distri.dal.mysql.integraltransferlog.IntegralTransferLogMapper;

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 IntegralTransferLogServiceImpl implements IntegralTransferLogService {

    @Resource
    private IntegralTransferLogMapper consumptionTransferLogMapper;

    @Resource
    private IntegralService integralService;

    @Resource
    private IntegralChangeLogService consumptionChangeLogService;

    @Resource
    private MemberUserService memberUserService;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Transactional
    public Long createConsumptionTransferLog(IntegralTransferLogSaveReqVO createReqVO) {
        // 自己不能转账自己
        Long loginUserId = getLoginUserId();
        verifyIntegral(createReqVO, loginUserId);

        // 消费分转成数据库存储的单位
        createReqVO.setConsumptionPoints(createReqVO.getConsumptionPoints() * DistriConstants.INTEGRAL_PAGE2DB);
        createReqVO.setTransferUserId(loginUserId);

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

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

        // 转让人钱包
        IntegralDO transferIntegral = integralService.selectByUser(createReqVO.getTransferUserId());
        Long afterTransferQuota = 0L;
        // 接收人钱包
        IntegralDO recipientIntegral = integralService.selectByUser(createReqVO.getRecipientUserId());
        Long afterRecipientQuota = 0L;

        // 根据不同积分进行转账
        switch (createReqVO.getIntegralType()) {
            case 1:     //红
                updateQuota(transferIntegral, recipientIntegral, createReqVO, "redCurrentQuota");
                afterTransferQuota = transferIntegral.getRedCurrentQuota();
                afterRecipientQuota = recipientIntegral.getRedCurrentQuota();
                break;
            case 2:     //黄
                updateQuota(transferIntegral, recipientIntegral, createReqVO, "yellowCurrentQuota");
                afterTransferQuota = transferIntegral.getYellowCurrentQuota();
                afterRecipientQuota = recipientIntegral.getYellowCurrentQuota();
                break;
            case 3:     //绿
                updateQuota(transferIntegral, recipientIntegral, createReqVO, "greenCurrentQuota");
                afterTransferQuota = transferIntegral.getGreenCurrentQuota();
                afterRecipientQuota = recipientIntegral.getGreenCurrentQuota();
                break;
        }

        // 更新积分
        integralService.updateIntegral(BeanUtils.toBean(transferIntegral, IntegralSaveReqVO.class));
        integralService.updateIntegral(BeanUtils.toBean(recipientIntegral, IntegralSaveReqVO.class));

        // 记录变动日志
        logIntegralChange(createReqVO, transferIntegral, recipientIntegral);

        // 插入转账记录
        IntegralTransferLogDO consumptionTransferLog = BeanUtils.toBean(createReqVO, IntegralTransferLogDO.class);
        consumptionTransferLog.setConsumptionPoints(-consumptionTransferLog.getConsumptionPoints());
        consumptionTransferLog.setRecipientPoints(-consumptionTransferLog.getConsumptionPoints());
        consumptionTransferLog.setAfterTransferConsumptionPoints(afterTransferQuota);
        consumptionTransferLog.setAfterRecipientConsumptionPoints(afterRecipientQuota);
        consumptionTransferLogMapper.insert(consumptionTransferLog);

        // 返回
        return consumptionTransferLog.getId();
    }

    /**
     * 更新积分配额
     * @param transferIntegral 转让积分的对象
     * @param recipientIntegral 接收积分的对象
     * @param createReqVO 积分转让请求对象，包含转让所需的详细信息
     * @param quotaField 字符串形式的配额字段名，用于通过反射获取和设置字段值
     */
    private void updateQuota(IntegralDO transferIntegral, IntegralDO recipientIntegral, IntegralTransferLogSaveReqVO createReqVO, String quotaField) {
        // 获取消费积分
        Long consumptionPoints = createReqVO.getConsumptionPoints();

        try{
            // 通过反射获取转让方和接收方的指定配额字段值
            Long transferQuota = (Long) ReflectionUtils.getFieldValue(transferIntegral, quotaField);
            Long recipientQuota = (Long) ReflectionUtils.getFieldValue(recipientIntegral, quotaField);

            // 验证转让方的配额是否足够
            verifyIntegral(transferQuota, consumptionPoints);

            // 更新转让方和接收方的配额
            ReflectionUtils.setFieldValue(transferIntegral, quotaField, transferQuota - consumptionPoints);
            ReflectionUtils.setFieldValue(recipientIntegral, quotaField, recipientQuota + consumptionPoints);
        } catch (Exception e){
            // 如果在积分转让过程中发生异常，抛出运行时异常
            throw new RuntimeException("积分转让异常");
        }
    }

    private void logIntegralChange(IntegralTransferLogSaveReqVO createReqVO, IntegralDO transferIntegral, IntegralDO recipientIntegral) {
        Long consumptionPoints = createReqVO.getConsumptionPoints();
        Long afterTransferConsumptionPoints = getQuotaValue(transferIntegral, createReqVO.getIntegralType());
        Long afterRecipientConsumptionPoints = getQuotaValue(recipientIntegral, createReqVO.getIntegralType());

        // 记录转让人变动日志
        consumptionChangeLogService.createIntegralChangeLog(
                IntegralChangeLogSaveReqVO.builder()
                        .userId(transferIntegral.getUserId())
                        .integralPoints(-consumptionPoints)
                        .practicalIntegralPoints(-consumptionPoints)
                        .afterIntegralPoints(afterTransferConsumptionPoints)
                        .integralStatus(IntegralEnum.INTEGRAL_TRANSFER.getType())
                        .generateUserId(recipientIntegral.getUserId())
                        .integralType(createReqVO.getIntegralType())
                        .build()
        );

        // 记录接收人变动日志
        consumptionChangeLogService.createIntegralChangeLog(
                IntegralChangeLogSaveReqVO.builder()
                        .userId(recipientIntegral.getUserId())
                        .integralPoints(consumptionPoints)
                        .practicalIntegralPoints(consumptionPoints)
                        .afterIntegralPoints(afterRecipientConsumptionPoints)
                        .integralStatus(IntegralEnum.INTEGRAL_RECIPIENT.getType())
                        .generateUserId(transferIntegral.getUserId())
                        .integralType(createReqVO.getIntegralType())
                        .build()
        );
    }

    private Long getQuotaValue(IntegralDO integral, int integralType) {
        switch (integralType) {
            case 1: return integral.getRedCurrentQuota();
            case 2: return integral.getYellowCurrentQuota();
            case 3: return integral.getGreenCurrentQuota();
            default: return 0L;
        }
    }


    /**
     * 校验用户
     * @param createReqVO 转让信息
     * @param loginUserId 用户id
     */
    private void verifyIntegral(IntegralTransferLogSaveReqVO createReqVO, Long loginUserId) {
        if (loginUserId != null && loginUserId.equals(createReqVO.getRecipientUserId())) {
            ErrorCode ERROR = new ErrorCode(1_002_030_037, "自己不能转账给自己");
            throw exception(ERROR);
        }
    }

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

    @Override
    public PageResult<IntegralTransferLogDO> getConsumptionTransferLogPage(IntegralTransferLogPageReqVO pageReqVO) {
        pageReqVO.setTransferUserId(getLoginUserId());
        return consumptionTransferLogMapper.selectPage(pageReqVO);
    }

}