package com.czg.service.account.service.impl;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.czg.account.dto.points.OrderDeductionPointsDTO;
import com.czg.account.entity.MemberPoints;
import com.czg.account.entity.MemberPointsLog;
import com.czg.account.entity.PointsBasicSetting;
import com.czg.account.entity.ShopInfo;
import com.czg.account.enums.PointUserGroupEnum;
import com.czg.account.param.MemberPointsParam;
import com.czg.account.service.MemberPointsService;
import com.czg.account.service.ShopInfoService;
import com.czg.enums.YesNoEnum;
import com.czg.exception.CzgException;
import com.czg.order.entity.OrderInfo;
import com.czg.sa.StpKit;
import com.czg.service.account.mapper.MemberPointsLogMapper;
import com.czg.service.account.mapper.MemberPointsMapper;
import com.czg.service.account.mapper.PointsBasicSettingMapper;
import com.czg.utils.PageUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;

/**
 * 会员积分变动记录
 *
 * @author Tankaikai tankaikai@aliyun.com
 * @since 1.0 2025-02-25
 */
@DubboService
public class MemberPointsServiceImpl extends ServiceImpl<MemberPointsMapper, MemberPoints> implements MemberPointsService {

    @Resource
    private PointsBasicSettingMapper pointsBasicSettingMapper;
    @Resource
    private MemberPointsLogMapper memberPointsLogMapper;
    @Resource
    private ShopInfoService shopInfoService;

    private QueryWrapper buildQueryWrapper(MemberPointsParam param) {
        QueryWrapper queryWrapper = PageUtil.buildSortQueryWrapper();
        if (StrUtil.isNotBlank(param.getNickName())) {
            queryWrapper.like(MemberPoints::getNickName, param.getNickName());
        }
        if (StrUtil.isNotBlank(param.getPhone())) {
            queryWrapper.like(MemberPoints::getPhone, param.getPhone());
        }

        Long shopId = StpKit.USER.getShopId();
        ShopInfo byId = shopInfoService.getById(shopId);
        if (byId == null) {
            throw new CzgException("店铺不存在");
        }
        if (byId.getIsEnableVipSync().equals(1)) {
            shopId = byId.getMainId();
        }
        queryWrapper.eq(MemberPoints::getSourceShopId, shopId);
        queryWrapper.orderBy(MemberPoints::getShopUserId, false);
        return queryWrapper;
    }


    @Override
    public Page<MemberPoints> getMemberPointsPage(MemberPointsParam param) {
        QueryWrapper queryWrapper = buildQueryWrapper(param);
        return super.page(PageUtil.buildPage(), queryWrapper);
    }

    @Override
    public MemberPoints initMemberPoints(Long shopUserId) {
        MemberPoints entity = super.getOne(query().eq(MemberPoints::getShopUserId, shopUserId));
        if (entity == null) {
            throw new CzgException("会员信息不存在");
        }
        if (entity.getAccountPoints() == null) {
            entity.setAccountPoints(0);
            super.updateById(entity);
        }
        return entity;
    }

    @Override
    public MemberPoints getMemberPoints(Long shopUserId) {
        return initMemberPoints(shopUserId);
    }

    @Override
    public OrderDeductionPointsDTO getMemberUsablePoints(Long shopUserId, BigDecimal orderAmount) {
        MemberPoints entity = initMemberPoints(shopUserId);
        Long shopId = entity.getMainShopId();
        Integer accountPoints = entity.getAccountPoints();
        OrderDeductionPointsDTO dto = new OrderDeductionPointsDTO();
        dto.setAccountPoints(accountPoints);
        dto.setUsable(false);
        dto.setMaxDeductionAmount(BigDecimal.ZERO);
        dto.setMinDeductionAmount(BigDecimal.ZERO);
        PointsBasicSetting basic = pointsBasicSettingMapper.selectOneById(shopId);
        if (basic == null) {
            dto.setUnusableReason("商家未启用积分抵扣功能");
            return dto;
        }
        if (basic.getEnableDeduction() == 0) {
            dto.setUnusableReason("商家未启用积分抵扣功能");
            return dto;
        }
        if (PointUserGroupEnum.VIP.value().equals(basic.getDeductionGroup()) && ObjectUtil.defaultIfNull(entity.getVip(), YesNoEnum.NO.value()) != YesNoEnum.YES.value()) {
            dto.setUnusableReason("仅VIP用户可用");
            return dto;
        }
        dto.setMinPaymentAmount(basic.getMinPaymentAmount());
        dto.setOrderAmount(orderAmount);
        if (NumberUtil.isLess(orderAmount, basic.getMinPaymentAmount())) {
            dto.setUnusableReason(StrUtil.format("实付金额不足￥{}元，无法进行抵扣", basic.getMinPaymentAmount()));
            return dto;
        }
        dto.setEquivalentPoints(basic.getEquivalentPoints());
        // 最低抵扣积分 = 1元等值积分 = 1.0元
        dto.setMinDeductionPoints(basic.getEquivalentPoints());
        // 计算抵扣门槛=?元
        dto.setMinDeductionAmount(BigDecimal.ONE);
        if (accountPoints == 0 || accountPoints < dto.getMinDeductionPoints()) {
            dto.setUnusableReason("积分不足或小于最低使用门槛" + dto.getMinDeductionPoints());
            return dto;
        }

        if (basic.getEquivalentPoints() == 0) {
            return dto;
        }
        // 下单抵扣积分比例 1元=?积分
        Integer equivalentPoints = basic.getEquivalentPoints();
        // 计算账户积分=?元
        BigDecimal accountYuan = NumberUtil.div(accountPoints, equivalentPoints);
        // 下单最高抵扣比例
        BigDecimal maxDeductionRatio = basic.getMaxDeductionRatio();
        // 计算订单最多可以抵扣多少元，向下取整
        BigDecimal orderYuan = NumberUtil.roundDown(NumberUtil.mul(orderAmount, NumberUtil.div(maxDeductionRatio, new BigDecimal("100"))), 0);
        // 积分余额足够
        if (NumberUtil.isGreaterOrEqual(accountYuan, orderYuan)) {
            dto.setMaxDeductionAmount(orderYuan);
        } else {
            dto.setMaxDeductionAmount(NumberUtil.roundDown(accountYuan, 0));
        }
        if (NumberUtil.isLess(dto.getMaxDeductionAmount(), BigDecimal.ONE)) {
            dto.setUnusableReason("积分不足1元，无法进行抵扣");
            return dto;
        }
        dto.setUsable(true);
        // 计算最多可抵扣的积分
        BigDecimal mul = NumberUtil.mul(dto.getMaxDeductionAmount(), equivalentPoints);
        BigDecimal round = NumberUtil.round(mul, 0, RoundingMode.CEILING);
        dto.setMaxUsablePoints(round.intValue());
        return dto;
    }

    @Override
    public int calcUsedPoints(Long shopUserId, BigDecimal orderAmount, BigDecimal deductionAmount) {
        OrderDeductionPointsDTO core = getMemberUsablePoints(shopUserId, orderAmount);
        if (!core.getUsable()) {
            throw new CzgException(core.getUnusableReason());
        }
        if (NumberUtil.isGreater(deductionAmount, core.getMaxDeductionAmount())) {
            throw new CzgException(StrUtil.format("抵扣金额不能超过最大抵扣金额{}元", core.getMaxDeductionAmount()));
        }
        if (NumberUtil.isGreater(deductionAmount, orderAmount)) {
            throw new CzgException(StrUtil.format("抵扣金额不能超过订单金额{}元", orderAmount));
        }
        // 计算可抵扣的积分
        BigDecimal mul = NumberUtil.mul(deductionAmount, core.getEquivalentPoints());
        BigDecimal round = NumberUtil.round(mul, 0, RoundingMode.CEILING);
        return round.intValue();
    }

    @Override
    public BigDecimal calcDeductionAmount(Long shopUserId, BigDecimal orderAmount, int points) {
        OrderDeductionPointsDTO core = getMemberUsablePoints(shopUserId, orderAmount);
        if (!core.getUsable()) {
            throw new CzgException(core.getUnusableReason());
        }
        if (points < core.getMinDeductionPoints()) {
            throw new CzgException(StrUtil.format("使用积分不能低于使用门槛(每次最少使用{}积分)", core.getMinDeductionPoints()));
        }
        if (points > core.getMaxUsablePoints()) {
            throw new CzgException(StrUtil.format("使用积分不能超过最大使用限制{}", core.getMaxUsablePoints()));
        }
        BigDecimal money = NumberUtil.mul(points, NumberUtil.div(BigDecimal.ONE, core.getEquivalentPoints()));
        BigDecimal maxDeductionAmount = NumberUtil.roundDown(money, 0);
        if (NumberUtil.isGreater(maxDeductionAmount, core.getMaxDeductionAmount())) {
            return core.getMaxDeductionAmount();
        }
        return maxDeductionAmount;
    }

    @Override
    public boolean deductPoints(Long shopUserId, int points, String content, OrderInfo orderInfo) {
        MemberPoints entity = initMemberPoints(shopUserId);
        // 扣除账户积分
        entity.setAccountPoints(entity.getAccountPoints() - points);
        entity.setLastPointsChangeTime(LocalDateTime.now());
        entity.setLastFloatPoints(-points);

        // 记录积分变动记录
        MemberPointsLog log = new MemberPointsLog();
        log.setShopId(entity.getSourceShopId());
        log.setShopUserId(entity.getShopUserId());
        log.setContent(content);
        log.setFloatType("subtract");
        log.setFloatPoints(-points);
        // 有关联订单的需要回置订单表的相关积分使用字段
        if (orderInfo != null) {
            log.setOrderNo(orderInfo.getOrderNo());
            // TODO 是否需要回执“使用的积分数量(points_num)”和“积分抵扣金额(points_discount_amount)”,目前不清楚是创建订单的时候置入还是支付完成后回调时置入需要商议后决定
        }
        super.updateById(entity);
        memberPointsLogMapper.insert(log);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addPoints(Long shopUserId, int points, String content, OrderInfo orderInfo) {
        MemberPoints entity = initMemberPoints(shopUserId);
        // 增加账户积分
        entity.setAccountPoints(entity.getAccountPoints() + points);
        entity.setLastPointsChangeTime(LocalDateTime.now());
        entity.setLastFloatPoints(points);
        // 记录积分变动记录
        MemberPointsLog log = new MemberPointsLog();
        log.setShopId(entity.getSourceShopId());
        log.setShopUserId(entity.getShopUserId());
        log.setContent(content);
        log.setFloatType("add");
        log.setFloatPoints(points);
        // 有关联订单的需要回置订单表的相关积分使用字段
        if (orderInfo != null) {
            log.setOrderNo(orderInfo.getOrderNo());
        }
        super.updateById(entity);
        memberPointsLogMapper.insert(log);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void consumeAwardPoints(Long shopUserId, OrderInfo orderInfo) {
        if (orderInfo == null) {
            throw new CzgException("订单不存在");
        }
        BigDecimal payAmount = orderInfo.getPayAmount();
        if (NumberUtil.isLessOrEqual(payAmount, BigDecimal.ZERO)) {
            return;
        }
        PointsBasicSetting basicSetting = pointsBasicSettingMapper.selectOneById(orderInfo.getShopId());
        if (basicSetting == null) {
            return;
        }
        Integer enableRewards = basicSetting.getEnableRewards();
        if (enableRewards == 0) {
            return;
        }
        String rewardsGroup = basicSetting.getRewardsGroup();
        MemberPoints entity;
        try {
            entity = initMemberPoints(shopUserId);
        } catch (Exception e) {
            return;
        }
        Integer vip = entity.getVip();
        if (PointUserGroupEnum.VIP.value().equals(rewardsGroup) && ObjectUtil.defaultIfNull(vip, 0) != 1) {
            return;
        }
        BigDecimal consumeAmount = basicSetting.getConsumeAmount();
        if (consumeAmount == null) {
            return;
        }
        if (NumberUtil.isLessOrEqual(consumeAmount, BigDecimal.ZERO)) {
            return;
        }
        BigDecimal awardPoints = NumberUtil.roundDown(NumberUtil.div(payAmount, consumeAmount), 0);
        addPoints(shopUserId, awardPoints.intValue(), StrUtil.format("消费￥{}送{}积分", payAmount, awardPoints.intValue()), orderInfo);
    }
}