package co.yixiang.yshop.module.product.service.productcouponrelation;


import co.yixiang.yshop.framework.mybatis.core.query.LambdaQueryWrapperX;
import co.yixiang.yshop.module.product.controller.app.couponrelation.vo.CartCouponDetailVO;
import co.yixiang.yshop.module.product.controller.app.couponrelation.vo.CartCouponVO;
import co.yixiang.yshop.module.product.controller.app.couponrelation.vo.UserCouponVO;
import co.yixiang.yshop.module.product.convert.storeproductcouponrelation.StoreProductCouponRelationConvert;
import co.yixiang.yshop.module.product.dal.dataobject.productcoupon.ProductCouponDO;
import co.yixiang.yshop.module.product.dal.dataobject.productcouponrelation.ProductCouponRelationDO;
import co.yixiang.yshop.module.product.dal.mysql.productcoupon.ProductCouponMapper;
import co.yixiang.yshop.module.product.dal.mysql.productcouponrelation.ProductCouponRelationMapper;
import co.yixiang.yshop.module.product.enums.coupon.CouponExpirationEnum;
import co.yixiang.yshop.module.product.enums.coupon.CouponReceiveTypeEnum;
import co.yixiang.yshop.module.product.enums.coupon.CouponScopeEnum;
import co.yixiang.yshop.module.product.enums.coupon.CouponTypeEnum;
import co.yixiang.yshop.module.product.enums.couponrelation.CouponIsUsedEnum;
import co.yixiang.yshop.module.product.enums.couponrelation.CouponSearchTypeEnum;
import co.yixiang.yshop.module.product.service.productcouponrelation.dto.CartCouponDTO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

import static co.yixiang.yshop.framework.common.exception.util.ServiceExceptionUtil.exception;
import static co.yixiang.yshop.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static co.yixiang.yshop.module.product.enums.ErrorCodeConstants.*;

/**
 * App优惠券记录 Service 接口
 *
 * @author moxiangrong
 */
@Service
@Validated
public class AppCouponRelationServiceImpl extends ServiceImpl<ProductCouponRelationMapper, ProductCouponRelationDO> implements AppCouponRelationService {

    @Resource
    private ProductCouponMapper couponMapper;

    @Override
    public void receiveCoupon(Long id, Long uid) {
        // 查询优惠券
        ProductCouponDO couponDO = couponMapper.selectById(id);
        if (couponDO == null) {
            throw exception(PRODUCT_COUPON_NOT_EXISTS);
        }
        if (couponDO.getNumber() < 1) throw exception(NOT_PRODUCT_COUPON_ERROR);

        long count = getReceivedCount(id, uid);

        if (CouponReceiveTypeEnum.UNLIMITED.getValue().equals(couponDO.getReceiveType())
                || count < couponDO.getLimitNumber()) {
            ProductCouponRelationDO relationDO = ProductCouponRelationDO.builder().couponId(id).uid(uid).build();
            relationDO.setReceiveTime(LocalDateTime.now());
            save(relationDO);
            couponMapper.updateById(ProductCouponDO.builder().id(couponDO.getId()).number(couponDO.getNumber() - 1).build());
        } else {
            throw exception(PRODUCT_COUPON_RECEIVE_ERROR);
        }
    }


    public long getReceivedCount(Long id, Long uid) {
        return count(new LambdaQueryWrapperX<ProductCouponRelationDO>()
                .eq(ProductCouponRelationDO::getCouponId, id).eq(ProductCouponRelationDO::getUid, uid));
    }

    @Override
    public void verificationCoupon(Long id) {
        ProductCouponRelationDO productCouponDO = getById(id);
        if (productCouponDO == null) {
            throw exception(PRODUCT_COUPON_NOT_EXISTS);
        }
        // 使用优惠券
        productCouponDO.setIsUsed(CouponIsUsedEnum.USED.getValue());
        productCouponDO.setUsageTime(LocalDateTime.now());
        updateById(productCouponDO);
    }

    @Override
    public void returnCoupon(Long id) {
        // 重置优惠券状态
        update(new LambdaUpdateWrapper<ProductCouponRelationDO>()
                .set(ProductCouponRelationDO::getIsUsed, CouponIsUsedEnum.NOT_USED.getValue())
                .set(ProductCouponRelationDO::getUsageTime, null)
                .eq(ProductCouponRelationDO::getId, id));
    }

    @Override
    public List<UserCouponVO> searchUserCoupon(Integer type, Long id) {
        List<UserCouponVO> userCouponVOS = this.baseMapper.searchUserCoupon(type, getLoginUserId(), id);
        for (UserCouponVO userCouponVO : userCouponVOS) {
            if (CouponExpirationEnum.BY_DAY.getValue().equals(userCouponVO.getExpirationType())) {
                // 计算失效时间
                LocalDateTime expirationTime =
                        userCouponVO.getReceiveTime().plus(userCouponVO.getExpirationDay(), ChronoUnit.DAYS);
                userCouponVO.setTakingEffectTime(userCouponVO.getReceiveTime());
                userCouponVO.setExpirationTime(expirationTime);
            }
        }
        return userCouponVOS;
    }

    @Override
    public List<CartCouponVO> searchCartCoupon(List<CartCouponDTO> cartCouponDtoList, Long id) {
        List<CartCouponVO> cartCouponVOList = new ArrayList<>();
        // 获取购物车中商品id集合
        Set<Long> productIds =
                cartCouponDtoList.stream().map(CartCouponDTO::getProductId).collect(Collectors.toSet());
        Map<Long, List<CartCouponDTO>> cartCouponDtoMap =
                cartCouponDtoList.stream().collect(Collectors.groupingBy(CartCouponDTO::getProductId));
        // 查询所有可使用的优惠券
        List<UserCouponVO> userCouponVOList = searchUserCoupon(CouponSearchTypeEnum.CAN_USE.getValue(), id);
        if (CollectionUtils.isEmpty(userCouponVOList)) {
            return cartCouponVOList;
        }
        cartCouponVOList = StoreProductCouponRelationConvert.INSTANCE.convert(userCouponVOList);
        // 过滤出可以使用的优惠券并计算优惠金额
        cartCouponVOList = cartCouponVOList.stream().filter(vo -> filter(productIds, vo, cartCouponDtoMap)).collect(Collectors.toList());
        // 优惠金额降序
        cartCouponVOList = cartCouponVOList.stream()
                .sorted(Comparator.comparing(CartCouponVO::getDiscountAmount).reversed()).collect(Collectors.toList());
        return cartCouponVOList;
    }

    @Override
    public List<Long> getProductIdList(Long id) {
        List<Long> productIdList = new ArrayList<>();
        ProductCouponRelationDO couponRelationDO = getById(id);
        ProductCouponDO couponDO = couponMapper.selectById(couponRelationDO.getCouponId());
        if (CouponScopeEnum.PRODUCT.getValue().equals(couponDO.getCouponScope())) {
            for (String s : couponDO.getScopeValues().split(",")) productIdList.add(Long.parseLong(s));
        }
        return productIdList;
    }

    @Override
    public Integer getCouponNumber(Long userId) {
        return this.baseMapper.getCouponNumber(userId);
    }


    /**
     * 过滤出可以使用的优惠券
     *
     * @param productIds       商品id集合
     * @param cartCouponVO     优惠券信息
     * @param cartCouponDtoMap 金额map
     * @return 过滤标识
     */
    private Boolean filter(Set<Long> productIds, CartCouponVO cartCouponVO, Map<Long, List<CartCouponDTO>> cartCouponDtoMap) {
        if (CouponScopeEnum.PRODUCT.getValue().equals(cartCouponVO.getCouponScope())) {
            Set<Long> ids = new HashSet<>();
            for (String s : cartCouponVO.getScopeValues().split(",")) ids.add(Long.parseLong(s));
            // 取交集
            ids.retainAll(productIds);
            if (CollectionUtils.isEmpty(ids)) return false;
            return compute(ids, cartCouponVO, cartCouponDtoMap);
        } else {
            return compute(productIds, cartCouponVO, cartCouponDtoMap);
        }
    }

    /**
     * 计算优惠金额
     *
     * @param productIds       商品id集合
     * @param cartCouponVO     优惠券信息
     * @param cartCouponDtoMap 金额map
     * @return 过滤标识
     */
    private Boolean compute(Set<Long> productIds, CartCouponVO cartCouponVO, Map<Long, List<CartCouponDTO>> cartCouponDtoMap) {
        // 累计金额
        BigDecimal amount = BigDecimal.ZERO;
        for (Long id : productIds)
            amount = amount.add(cartCouponDtoMap.get(id).stream().map(CartCouponDTO::getPrice).reduce(BigDecimal::add).get());
        // 是否满足门槛
        if (amount.compareTo(cartCouponVO.getThreshold()) < 0) return Boolean.FALSE;
        boolean couponTypeFlag = CouponTypeEnum.FULL_REDUCTION.getValue().equals(cartCouponVO.getCouponType());
        // 计算优惠金额
        BigDecimal discountAmount = BigDecimal.ZERO;
        List<CartCouponDetailVO> detailVOList = new ArrayList<>();
        for (Long id : productIds) {
            BigDecimal productCoupon = null;
            for (CartCouponDTO cartCouponDto : cartCouponDtoMap.get(id)) {
                if (couponTypeFlag) {
                    // 优惠金额不可超出商品总价
                    if (amount.compareTo(cartCouponVO.getCouponValue()) < 0) {
                        productCoupon = cartCouponDto.getPrice();
                    } else {
                        productCoupon = cartCouponVO.getCouponValue().multiply(cartCouponDto.getPrice().divide(amount, BigDecimal.ROUND_HALF_UP));
                    }
                } else {
                    productCoupon = cartCouponDto.getPrice().subtract(cartCouponDto.getPrice().multiply(cartCouponVO.getDiscount()).divide(BigDecimal.TEN));
                }
                discountAmount = discountAmount.add(productCoupon);
                detailVOList.add(CartCouponDetailVO.builder().productId(id).productAttrUnique(cartCouponDto.getProductAttrUnique()).productCoupon(productCoupon).build());
            }
        }
        cartCouponVO.setDiscountAmount(discountAmount);
        cartCouponVO.setDetailVOList(detailVOList);
        return Boolean.TRUE;
    }


}
