package com.sqx.modules.activity.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sqx.common.constant.MyConstant;
import com.sqx.common.exception.SqxException;
import com.sqx.common.utils.PageUtils;
import com.sqx.modules.activity.bo.ShopActivityBO;
import com.sqx.modules.activity.dao.ActivityDao;
import com.sqx.modules.activity.dto.ActivityDTO;
import com.sqx.modules.activity.dto.ActivityOfFullReductionDTO;
import com.sqx.modules.activity.dto.ActivityOfGlobalDiscountDTO;
import com.sqx.modules.activity.dto.ActivityOfTimeIntervalDTO;
import com.sqx.modules.activity.dto.ActivityQueryDTO;
import com.sqx.modules.activity.entity.Activity;
import com.sqx.modules.activity.entity.ActivityShop;
import com.sqx.modules.activity.enums.ActivityTypeEnum;
import com.sqx.modules.activity.enums.FullActivityTypeEnum;
import com.sqx.modules.activity.enums.LimitTypeEnum;
import com.sqx.modules.activity.enums.SuitTypeEnum;
import com.sqx.modules.activity.service.ActivityGoodsService;
import com.sqx.modules.activity.service.ActivityService;
import com.sqx.modules.activity.service.ActivityShopService;
import com.sqx.modules.activity.util.ActivityUtil;
import com.sqx.modules.activity.vo.ActivityOrderVO;
import com.sqx.modules.activity.vo.ActivityVO;
import com.sqx.modules.activity.vo.OrderSuitActivityVO;
import com.sqx.modules.order.entity.OrderGoods;
import com.sqx.modules.order.entity.TbOrder;
import com.sqx.modules.order.service.AppOrderService;
import lombok.RequiredArgsConstructor;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 活动
 * @author : codingliang
 * @date : 2024-6-17
 */
@Service
@RequiredArgsConstructor
public class ActivityServiceImpl extends ServiceImpl<ActivityDao, Activity> implements ActivityService {

    @Autowired
    private ActivityDao activityDao;

    private final ActivityShopService activityShopService;
    private final ActivityGoodsService activityGoodsService;
    private final AppOrderService orderService;

    @Override
    public ActivityVO getActivityById(Long id) {
        Activity activity = this.findById(id);

        return ActivityUtil.convertActivityEntityToVO(activity);
    }

    @Override
    public PageUtils pageActivity(ActivityQueryDTO queryDTO) {
        Page<Activity> pages = new Page<>(queryDTO.getPage(), queryDTO.getLimit());

        LambdaQueryWrapper<Activity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.like(StrUtil.isNotBlank(queryDTO.getTitle()), Activity::getTitle, queryDTO.getTitle());
        queryWrapper.eq(StrUtil.isNotBlank(queryDTO.getEnableFlag()), Activity::getEnableFlag, queryDTO.getEnableFlag());
        queryWrapper.eq(StrUtil.isNotBlank(queryDTO.getAllowShop()), Activity::getAllowShop, queryDTO.getAllowShop());
        queryWrapper.eq(StrUtil.isNotBlank(queryDTO.getType()), Activity::getType, queryDTO.getType());
        queryWrapper.eq(ObjectUtil.isNotNull(queryDTO.getStartTime()), Activity::getStartTime, queryDTO.getStartTime());
        queryWrapper.eq(ObjectUtil.isNotNull(queryDTO.getEndTime()), Activity::getEndTime, queryDTO.getEndTime());
        queryWrapper.orderByDesc(Activity::getCreateTime);

        IPage<Activity> page = this.page(pages, queryWrapper);
        PageUtils pageUtils = new PageUtils(page);

        List<ActivityVO> vos = pageUtils.getList().stream().map(e -> ActivityUtil.convertActivityEntityToVO((Activity) e)).collect(Collectors.toList());
        pageUtils.setList(vos);
        return pageUtils;
    }

    @Override
    public void addActivity(ActivityDTO activityDTO) {
        checkParam(activityDTO);

        Activity activity = new Activity();
        BeanUtils.copyProperties(activityDTO, activity);

        String config = getActivityConfig(activityDTO);
        activity.setConfig(config);
        activity.setDelFlag(MyConstant.NO);

        Date date = new Date();
        activity.setCreateTime(date);
        activity.setUpdateTime(date);

        this.save(activity);
    }

    @Override
    public void updateActivity(ActivityDTO activityDTO) {
        Activity oldActivity = findById(activityDTO.getId());

        if (!StrUtil.equals(oldActivity.getType(), activityDTO.getType())) {
            throw new SqxException("活动类型不能修改");
        }

        checkParam(activityDTO);
        Activity newActivity = new Activity();
        BeanUtils.copyProperties(activityDTO, newActivity);

        newActivity.setId(activityDTO.getId());
        newActivity.setConfig(getActivityConfig(activityDTO));
        newActivity.setDelFlag(MyConstant.NO);
        newActivity.setCreateTime(oldActivity.getCreateTime());
        newActivity.setUpdateTime(new Date());

        this.updateById(newActivity);
    }

    @Override
    public void updateActivityStatus(Long id, String flag) {
        findById(id);

        Activity newActivity = new Activity();
        newActivity.setId(id);
        newActivity.setEnableFlag(flag);

        this.updateById(newActivity);
    }

    @Override
    public void updateActivityJoinMethod(Long id, String flag) {
        findById(id);

        Activity newActivity = new Activity();
        newActivity.setId(id);
        newActivity.setAllowShop(flag);

        this.updateById(newActivity);
    }

    @Override
    public void deleteActivity(List<Long> activityIds) {
        // 判断待删除活动是否还有商家未退出
        List<ActivityShop> activityShops = activityShopService.getByActivityIds(activityIds);

        List<Long> refuseDeleteIds = activityIds.stream().filter(e -> activityShops.contains(e)).collect(Collectors.toList());
        if (refuseDeleteIds.size() > 0) {
            String errorInfo = refuseDeleteIds.stream().map(e -> String.valueOf(activityIds.indexOf(e))).collect(Collectors.joining(","));

            throw new SqxException("索引编号为【" + errorInfo + "】的活动下仍有商家，请退出后重试");
        }

        removeByIds(activityIds);
    }

    @Override
    public List<ShopActivityBO> getActivityListByShopIdAndGoodsId(Long shopId, Long goodsId) {
        List<ShopActivityBO> shopActivityBOS = activityShopService.getShopActivityBOByShopId(shopId);

        // 满减活动过滤
        return shopActivityBOS.stream().filter(shopActivityBO -> {
            // 满减优惠
            if (ActivityTypeEnum.FULL.getTypeCode().equals(shopActivityBO.getType())) {
                // 查询符合条件的商品
                List<Long> applyGoodsIds = activityGoodsService.getByActivityShopId(shopActivityBO.getActivityShopId());
                return applyGoodsIds.contains(goodsId);
            } else {
                return true;
            }
        }).collect(Collectors.toList());
    }

    @Override
    public List<OrderSuitActivityVO> getOrderSuitActivity(Long orderId) {
        TbOrder order = orderService.getById(orderId);
        if (ObjectUtil.isNull(order)) {
            throw new SqxException("无效的订单id");
        }

        List<OrderGoods> orderGoodsList = orderService.getOrderGoods(orderId);
        if (CollUtil.isEmpty(orderGoodsList)) {
            throw new SqxException("订单数据异常，请联系管理员");
        }

        // 订单所在店铺id
        Long shopId = order.getShopId();

        // 获取当前店铺参加的活动
        List<ShopActivityBO> curShopActivityBOs = activityShopService.getShopActivityBOByShopId(shopId);

        // 活动限制过滤
        curShopActivityBOs = curShopActivityBOs.stream()
                .filter(shopActivityBO -> activityLimitFilter(shopActivityBO, order))
                .collect(Collectors.toList());

        // 计算当前订单适用的所有可用活动优惠价格
        return curShopActivityBOs.stream()
                .map(shopActivityBO -> calcOrderSuitActivity(shopActivityBO, orderGoodsList))
                .filter(Objects::nonNull)
                .sorted(Comparator.comparing(OrderSuitActivityVO::getDiscountAmount).reversed())
                .collect(Collectors.toList());
    }

    @Override
    public OrderSuitActivityVO getOrderBestActivity(Long orderId) {
        List<OrderSuitActivityVO> suitActivity = getOrderSuitActivity(orderId);
        if (CollUtil.isEmpty(suitActivity)) {
            return null;
        } else {
            return suitActivity.get(0);
        }
    }

    /**
     * 根据id获取活动信息
     *    如id对应记录不存在，则抛出数据不存在异常
     * @param id 活动id
     * @return activity
     */
    private Activity findById(Long id) {
        Activity activity = this.getById(id);

        if (ObjectUtil.isNull(activity)) {
            throw new SqxException("无效的活动id");
        }

        return activity;
    }

    /**
     * 获取活动配置
     * @param activityDTO
     * @return 活动config json串
     */
    private String getActivityConfig(ActivityDTO activityDTO) {
        String config = null;
        String type = activityDTO.getType();
        if (ActivityTypeEnum.TIME.getTypeCode().equals(type)) {
            config = JSONUtil.toJsonStr(activityDTO.getTimeIntervalInfos());
        } else if (ActivityTypeEnum.FULL.getTypeCode().equals(type)) {
            config = JSONUtil.toJsonStr(activityDTO.getFullReductionInfo());
        } else if (ActivityTypeEnum.GLOBAL.getTypeCode().equals(type)) {
            config = JSONUtil.toJsonStr(activityDTO.getGlobalDiscountsInfo());
        }
        return config;
    }

    /**
     * 校验活动参数
     * @param activityDTO activityDTO
     */
    private void checkParam(ActivityDTO activityDTO) {
        checkActivityDate(activityDTO.getStartTime(), activityDTO.getEndTime());

        String type = activityDTO.getType();
        if (ActivityTypeEnum.TIME.getTypeCode().equals(type)) {
            checkTimeActivityParam(activityDTO.getTimeIntervalInfos());
        } else if (ActivityTypeEnum.FULL.getTypeCode().equals(type)) {
            checkFullActivityParam(activityDTO.getFullReductionInfo());
        } else if (ActivityTypeEnum.GLOBAL.getTypeCode().equals(type)) {
            checkGlobalActivityParam(activityDTO.getGlobalDiscountsInfo());
        }
    }

    /**
     * 校验活动时间
     * @param startTime 开始时间
     * @param endTime 结束时间
     */
    private void checkActivityDate(Date startTime, Date endTime) {
        // 结束时间必须在开始时间之后
        if (endTime.compareTo(startTime) <= 0) {
            throw new SqxException("活动结束时间必须在开始时间之后");
        }
    }

    /**
     * 校验全局优惠活动参数信息
     * @param globalDiscountsInfo 全局优惠信息
     */
    private void checkGlobalActivityParam(ActivityOfGlobalDiscountDTO globalDiscountsInfo) {
        if (ObjectUtil.isNull(globalDiscountsInfo)) {
            throw new SqxException("全场优惠信息不能为空");
        }

        if (globalDiscountsInfo.getDiscountRate().doubleValue() >= 10 || globalDiscountsInfo.getDiscountRate().doubleValue() <= 0) {
            throw new SqxException("全场优惠折扣只能为(0, 10)");
        }
    }

    /**
     * 校验满减优惠活动参数信息
     * @param fullReductionInfo 满减优惠信息
     */
    private void checkFullActivityParam(ActivityOfFullReductionDTO fullReductionInfo) {
        if (ObjectUtil.isNull(fullReductionInfo)) {
            throw new SqxException("满减优惠信息不能为空");
        }

        String type = fullReductionInfo.getType();
        if (FullActivityTypeEnum.DISCOUNT.getTypeCode().equals(type)) {
            Double discountContent = fullReductionInfo.getDiscountContent();
            if (discountContent.doubleValue() >= 10 || discountContent.doubleValue() <= 0) {
                throw new SqxException("满减优惠折扣只能为(0, 10)");
            }
        }
    }

    /**
     * 校验时段优惠活动参数信息
     * @param timeIntervalInfos 时段优惠信息
     */
    private void checkTimeActivityParam(List<ActivityOfTimeIntervalDTO> timeIntervalInfos) {
        if (CollUtil.isEmpty(timeIntervalInfos)) {
            throw new SqxException("时段优惠信息不能为空");
        }

        for (ActivityOfTimeIntervalDTO timeIntervalInfo : timeIntervalInfos) {
            if (timeIntervalInfo.getDiscountContent().doubleValue() == 0 || timeIntervalInfo.getDiscountContent().doubleValue() == 10) {
                throw new SqxException("时段优惠信息折扣内容取值范围(0-10)");
            }

            try {
                MyConstant.TIME_DTF.parse(timeIntervalInfo.getStartTime());
                MyConstant.TIME_DTF.parse(timeIntervalInfo.getEndTime());
            } catch (Exception e) {
                throw new SqxException("时间格式有误，startTime和endTime格式应为HH:mm");
            }
        }
    }

    /**
     * 活动限制过滤
     * @param shopActivityBO 店铺活动信息
     * @param order 订单信息
     * @return 订单是否适用当前活动
     */
    private boolean activityLimitFilter(ShopActivityBO shopActivityBO, TbOrder order) {
        // 是否仅针对第一次下单
        if (SuitTypeEnum.FIRST_ORDER.getTypeCode().equals(shopActivityBO.getSuitType())) {
            // 查询用户在该店铺消费次数
            int historyCount = orderService.countByShopIdAndUserId(order.getShopId(), order.getUserId(), order.getOrderId());
            return historyCount == 0;
        }

        // 是否有数量限制
        if (LimitTypeEnum.NUM_LIMIT.getTypeCode().equals(shopActivityBO.getLimitType())) {
            // 每天限制的数量
            Integer limitValue = shopActivityBO.getLimitValue();

            int curDateCount = orderService.countByShopIdAndActivityIdAndUserIdAndCurDate(order.getShopId(), shopActivityBO.getActivityId(), order.getUserId(), order.getOrderId());
            return limitValue.intValue() > curDateCount;
        }

        return true;
    }

    /**
     * 计算当前订单商品适用的活动优惠价格
     * @param shopActivityBO 店铺活动信息
     * @param orderGoodsList 订单商品列表
     * @return 订单适用活动信息
     */
    private OrderSuitActivityVO calcOrderSuitActivity(ShopActivityBO shopActivityBO, List<OrderGoods> orderGoodsList) {
        String type = shopActivityBO.getType();

        // 当前参与活动商品总价
        double curSuitGoodsTotalPrice = 0.0;
        // 符合当前活动的商品id集合
        List<Long> applyGoodsIds;
        // 符合当前活动的商品name集合
        List<String> applyGoodsNames;

        // 满减优惠
        if (ActivityTypeEnum.FULL.getTypeCode().equals(type)) {
            // 查询符合条件的商品
            List<Long> curShopActivityGoodsIds = activityGoodsService.getByActivityShopId(shopActivityBO.getActivityShopId());

            applyGoodsIds = orderGoodsList.stream()
                    .filter(orderGoods -> curShopActivityGoodsIds.contains(orderGoods.getGoodsId()))
                    .map(OrderGoods::getId)
                    .collect(Collectors.toList());

            curSuitGoodsTotalPrice = orderGoodsList.stream()
                    .filter(orderGoods -> curShopActivityGoodsIds.contains(orderGoods.getGoodsId()))
                    .mapToDouble(orderGoods -> orderGoods.getGoodsPrice().multiply(new BigDecimal(orderGoods.getGoodsNum().toString())).doubleValue())
                    .sum();

            applyGoodsNames = orderGoodsList.stream()
                    .filter(orderGoods -> curShopActivityGoodsIds.contains(orderGoods.getGoodsId()))
                    .map(OrderGoods::getGoodsName)
                    .collect(Collectors.toList());
        }
        // 全场优惠和时段优惠所有的商品均参与活动
        else {
            applyGoodsIds = orderGoodsList.stream()
                    .map(OrderGoods::getId)
                    .collect(Collectors.toList());

            curSuitGoodsTotalPrice = orderGoodsList.stream()
                    .mapToDouble(orderGoods -> orderGoods.getGoodsPrice().multiply(new BigDecimal(orderGoods.getGoodsNum().toString())).doubleValue())
                    .sum();

            applyGoodsNames = orderGoodsList.stream()
                    .map(OrderGoods::getGoodsName)
                    .collect(Collectors.toList());
        }

        // 优惠金额
        Double discountAmount = 0.0;

        // 计算活动优惠金额
        if (ActivityTypeEnum.TIME.getTypeCode().equals(type)) {
            ActivityOfTimeIntervalDTO curTimeTimeActivity = ActivityUtil.getCurTimeTimeActivity(shopActivityBO.getTimeIntervalInfos());

            if (ObjectUtil.isNull(curTimeTimeActivity)) {
                return null;
            }

            Double discountContent = curTimeTimeActivity.getDiscountContent() / 10.0;
            discountAmount = curSuitGoodsTotalPrice - (curSuitGoodsTotalPrice * discountContent);
        } else if (ActivityTypeEnum.FULL.getTypeCode().equals(type)) {
            ActivityOfFullReductionDTO fullReductionInfo = shopActivityBO.getFullReductionInfo();

            // 判断当前商品总价满足最低满减金额
            if (curSuitGoodsTotalPrice - fullReductionInfo.getMinAmount() < 0) {
                return null;
            }

            if (FullActivityTypeEnum.DISCOUNT.getTypeCode().equals(fullReductionInfo.getType())) {
                Double discountContent = fullReductionInfo.getDiscountContent() / 10.0;
                discountAmount = curSuitGoodsTotalPrice - (curSuitGoodsTotalPrice * discountContent);
            } else {
                Double discountContent = fullReductionInfo.getDiscountContent();

                // 如果满减金额大于当前商品的总金额，则优惠金额为当前商品的总金额，否则优惠金额为当前配置的金额
                if (discountContent > curSuitGoodsTotalPrice) {
                    discountAmount = curSuitGoodsTotalPrice;
                } else {
                    discountAmount = discountContent;
                }
            }
        } else if (ActivityTypeEnum.GLOBAL.getTypeCode().equals(type)) {
            ActivityOfGlobalDiscountDTO globalDiscountsInfo = shopActivityBO.getGlobalDiscountsInfo();
            Double discountRate = globalDiscountsInfo.getDiscountRate() / 10.0;
            discountAmount = curSuitGoodsTotalPrice - (curSuitGoodsTotalPrice * discountRate);
        }

        OrderSuitActivityVO vo = new OrderSuitActivityVO();
        vo.setActivityId(shopActivityBO.getActivityId());
        vo.setActivityTitle(shopActivityBO.getTitle());
        vo.setActivityEndTime(shopActivityBO.getEndTime());
        vo.setActivityType(type);
        vo.setDiscountAmount(Double.parseDouble(String.format("%.2f", discountAmount)));
        vo.setGoodsIds(applyGoodsIds);
        vo.setGoodsNames(applyGoodsNames);
        return vo;
    }

    @Override
    public ActivityOrderVO selectByOrder(Long orderId) {
        return activityDao.selectByOrder(orderId);
    }

    @Override
    public List<ActivityVO> getActivityByIds(List<Long> ids) {
        List<Activity> activities = baseMapper.selectBatchIds(ids);
        return activities.stream().map(ActivityUtil::convertActivityEntityToVO).collect(Collectors.toList());
    }
}
