package com.kym.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.kym.common.exception.BusinessException;
import com.kym.common.utils.CommUtil;
import com.kym.entity.ActivityStation;
import com.kym.entity.Coupon;
import com.kym.entity.queryParams.CouponQueryParam;
import com.kym.entity.vo.UserCouponsIssueVo;
import com.kym.entity.common.PageBean;
import com.kym.entity.common.RedisKeys;
import com.kym.entity.User;
import com.kym.entity.UserCoupon;
import com.kym.mapper.CouponMapper;
import com.kym.service.ActivityService;
import com.kym.service.CouponService;
import com.kym.service.UserService;
import com.kym.service.mq.producer.UserCouponSender;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;


/**
 * <p>
 * 优惠券表 服务实现类
 * </p>
 *
 * @author skyline
 * @since 2024-04-25
 */
@Service
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements CouponService {

    private final UserService userService;
    private final ActivityService activityService;
    private final UserCouponSender userCouponSender;
    private final RedisTemplate<String, String> redisTemplate;

    public CouponServiceImpl(UserService userService, @Lazy ActivityService activityService,
                             UserCouponSender userCouponSender, RedisTemplate<String, String> redisTemplate) {
        this.userService = userService;
        this.activityService = activityService;
        this.userCouponSender = userCouponSender;
        this.redisTemplate = redisTemplate;
    }


    @Override
    public PageBean<Coupon> listCoupon(CouponQueryParam params) {
        PageHelper.startPage(params.getPageNum(), params.getPageSize());
        var res = lambdaQuery()
                .eq(CommUtil.isNotEmptyAndNull(params.getCouponType()), Coupon::getCouponType, params.getCouponType())
                .gt(Coupon::getStatus, Coupon.STATUS_未开始)
                .like(CommUtil.isNotEmptyAndNull(params.getCouponName()), Coupon::getName, params.getCouponName())
                .orderByDesc(Coupon::getStartTime)
                .list();
        return new PageBean<>(res);
    }

    @Override
    public void issueCoupons(UserCouponsIssueVo userCouponsIssue) {
        var coupons = lambdaQuery().in(Coupon::getId, userCouponsIssue.getCouponIds()).list();

        // 校验优惠券领取方式
        if (coupons.stream().anyMatch(coupon -> coupon.getReceiveType().equals(Coupon.RECEIVE_TYPE_主动领取))) {
            throw new BusinessException("包含限自主领取的优惠券");
        }

        // 校验发放数量是否不大于券剩余数量（数量=0代表不限量）
        var flag = coupons.stream().findAny().filter(coupon -> coupon.getQuantity() > 0 && userCouponsIssue.getUserIds().size() > (coupon.getQuantity() - coupon.getClaimedQuantity()));
        if (flag.isPresent()) {
            throw new BusinessException("优惠券剩余数量不足，优惠券名称：%s，剩余:%d，发放数量：%d".formatted(flag.get().getName(), flag.get().getQuantity() - flag.get().getClaimedQuantity(), userCouponsIssue.getUserIds().size()));
        }

        List<User> users = new ArrayList<>();
        if (CommUtil.isNotEmptyAndNull(userCouponsIssue.getUserIds())) {
            users = userService.lambdaQuery().in(User::getId, userCouponsIssue.getUserIds()).list();
        } else if (CommUtil.isNotEmptyAndNull(userCouponsIssue.getPhones())) {
            users = userService.lambdaQuery().in(User::getMobilePhone, userCouponsIssue.getPhones()).list();
        }

        var activityList = activityService.listByIds(coupons.stream().map(Coupon::getActivityId).toList());

        // 更新优惠券发放数量
        lambdaUpdate().setSql("claimed_quantity = claimed_quantity + %d".formatted(users.size()))
                .in(Coupon::getId, coupons.stream().map(Coupon::getId).toList()).update();

        users.forEach(user -> {
            for (Coupon coupon : coupons) {
                var userCoupon = new UserCoupon();
                userCoupon.setActivityId(coupon.getActivityId());
                userCoupon.setActivityName(Objects.requireNonNull(activityList.stream().filter(activity -> activity.getId().equals(coupon.getActivityId())).findFirst().orElse(null)).getName());
                userCoupon.setCouponId(coupon.getId());
                userCoupon.setCouponName(coupon.getName());
                userCoupon.setUserId(user.getId());
                userCoupon.setStartTime(LocalDateTime.now());
                userCoupon.setEndTime(LocalDateTime.now().with(LocalTime.MAX).plusDays(coupon.getValidity() - 1));
                userCoupon.setValidity(coupon.getValidity());
                userCoupon.setCouponType(coupon.getCouponType());
                userCoupon.setDiscount(coupon.getDiscount());
                userCoupon.setMinServiceMoney(coupon.getMinServiceMoney());
                userCoupon.setAllowStacke(coupon.getAllowStacke());
                userCoupon.setRemark(coupon.getRemark());
                userCoupon.setStatus(coupon.getStatus());
                userCouponSender.sendMessage(userCoupon);
            }
        });
    }

    @Override
    public PageBean<Coupon> listAvailableCoupons(CouponQueryParam params) {
        if (CommUtil.isEmptyOrNull(params.getStationId())) {
            throw new BusinessException("请选择洗车站点");
        }
        PageHelper.startPage(params.getPageNum(), params.getPageSize());
        MPJLambdaWrapper<Coupon> wrapper = JoinWrappers.lambda(Coupon.class)
                .selectAll(Coupon.class)
                .leftJoin(ActivityStation.class, ActivityStation::getActivityId, Coupon::getActivityId)
                .eq(Coupon::getReceiveType, Coupon.RECEIVE_TYPE_主动领取)
                .in(Coupon::getStatus, Coupon.STATUS_未开始, Coupon.STATUS_进行中)
                .eq(ActivityStation::getStationId, params.getStationId());
        List<Coupon> res = this.selectJoinList(Coupon.class, wrapper);

        //已领取的标识出来
        long userId = StpUtil.getLoginIdAsLong();
        res.forEach(coupon -> coupon.setCollected(Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(RedisKeys.COUPON_ID_TO_USERS, coupon.getId() + ":" + userId))));
        return new PageBean<>(res);
    }

    @Override
    public void usedQuantifyCount(Long couponId) {
        lambdaUpdate().setSql("used_quantity = used_quantity + 1").eq(Coupon::getId, couponId).update();
    }

}
