package com.liquidnet.service.slime.service.impl;

import com.github.pagehelper.PageInfo;
import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.base.constant.RedisKeyExpireConst;
import com.liquidnet.service.kylin.constant.GroupBuyStatusConst;
import com.liquidnet.service.kylin.constant.KylinRedisConst;
import com.liquidnet.service.kylin.dto.param.KylinGroupBuyActivityCreateParam;
import com.liquidnet.service.kylin.dto.param.KylinGroupBuyActivityUpdateParam;
import com.liquidnet.service.kylin.dto.vo.groupbuy.KylinGroupBuyActivityVo;
import com.liquidnet.service.kylin.dto.vo.groupbuy.KylinGroupBuyDiscountVo;
import com.liquidnet.service.kylin.dto.vo.groupbuy.KylinGroupBuyMemberVo;
import com.liquidnet.service.kylin.dto.vo.groupbuy.KylinGroupBuyOrderVo;
import com.liquidnet.service.kylin.dto.vo.groupbuy.KylinGroupBuyPartnerOrderDetailVo;
import com.liquidnet.service.kylin.dto.vo.groupbuy.KylinGroupBuyPartnerOrderListVo;
import com.liquidnet.service.kylin.dto.vo.groupbuy.KylinGroupBuyPartnerOrderMemberVo;
import com.liquidnet.service.kylin.dto.vo.groupbuy.KylinGroupBuyPartnerOrderStatsVo;
import com.liquidnet.service.kylin.dto.vo.middle.KylinTicketTimesVo;
import com.liquidnet.service.kylin.dto.vo.middle.KylinTicketVo;
import com.liquidnet.service.kylin.dto.vo.mongo.KylinOrderTicketVo;
import com.liquidnet.service.kylin.dto.vo.mongo.KylinPerformanceVo;
import com.liquidnet.service.kylin.service.partner.IKylinGroupBuyPartnerService;
import com.liquidnet.service.slime.util.GroupBuyOrderExcelUtil;
import com.liquidnet.service.slime.util.ObjectUtil;
import com.liquidnet.service.slime.util.QueueUtil;
import com.liquidnet.service.slime.util.RedisSlimeUtils;
import com.mongodb.BasicDBObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
public class KylinGroupBuyPartnerServiceImpl implements IKylinGroupBuyPartnerService {

    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private RedisSlimeUtils redisSlimeUtils;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private QueueUtil queueUtil;

    @Override
    public ResponseDto<PageInfo<KylinGroupBuyActivityVo>> list(String name, Integer status, Integer page, Integer size) {
        Criteria criteria = new Criteria();
        if (status != null) {
            criteria.and("status").is(status);
        }
        if (name != null && !name.trim().isEmpty()) {
            criteria.and("name").regex(".*" + name.trim() + ".*", "i");
        }
        Query countQuery = Query.query(criteria);
        long total = mongoTemplate.count(countQuery, KylinGroupBuyActivityVo.class, KylinGroupBuyActivityVo.class.getSimpleName());
        Query dataQuery = Query.query(criteria).with(Sort.by(Sort.Direction.DESC, "createdAt"));
        if (page != null && size != null && page > 0 && size > 0) {
            dataQuery.skip((long) (page - 1) * size).limit(size);
        }
        List<KylinGroupBuyActivityVo> list = mongoTemplate.find(dataQuery,
                KylinGroupBuyActivityVo.class, KylinGroupBuyActivityVo.class.getSimpleName());
        if (!list.isEmpty()) {
            List<String> activityIds = list.stream().map(KylinGroupBuyActivityVo::getActivityId).collect(Collectors.toList());
            Map<String, Long> orderCountMap = new HashMap<>();
            for (String activityId : activityIds) {
                long count = mongoTemplate.count(Query.query(Criteria.where("activityId").is(activityId)),
                        KylinGroupBuyOrderVo.class.getSimpleName());
                orderCountMap.put(activityId, count);
            }
            for (KylinGroupBuyActivityVo vo : list) {
                vo.setOrderCount(orderCountMap.getOrDefault(vo.getActivityId(), 0L));
                KylinPerformanceVo performanceVo = redisSlimeUtils.getPerformanceVo(vo.getPerformanceId());
                vo.setPerformanceName(performanceVo != null ? performanceVo.getTitle() : null);
            }
        }
        PageInfo<KylinGroupBuyActivityVo> pageInfo = new PageInfo<>(list);
        pageInfo.setTotal(total);
        return ResponseDto.success(pageInfo);
    }

    @Override
    public ResponseDto<Map<String, Long>> stats() {
        Map<String, Long> result = new HashMap<>();
        String collection = KylinGroupBuyActivityVo.class.getSimpleName();
        result.put("totalActivities", mongoTemplate.count(Query.query(new Criteria()), collection));
        result.put("activeCount", mongoTemplate.count(
                Query.query(Criteria.where("status").is(GroupBuyStatusConst.ActivityStatus.ACTIVE)), collection));
        result.put("endedCount", mongoTemplate.count(
                Query.query(Criteria.where("status").is(GroupBuyStatusConst.ActivityStatus.ENDED)), collection));
        result.put("orderCount", mongoTemplate.count(Query.query(new Criteria()),
                KylinGroupBuyOrderVo.class.getSimpleName()));
        return ResponseDto.success(result);
    }

    @Override
    public ResponseDto<KylinGroupBuyActivityVo> detail(String activityId) {
        KylinGroupBuyActivityVo vo = findActivity(activityId);
        return vo == null ? ResponseDto.failure("拼团活动不存在") : ResponseDto.success(vo);
    }

    @Override
    public ResponseDto<KylinGroupBuyActivityVo> create(KylinGroupBuyActivityCreateParam param) {
        ResponseDto<Boolean> validate = validateParam(param);
        if (!validate.isSuccess()) {
            return ResponseDto.failure(validate.getMessage());
        }
        String now = now();
        KylinGroupBuyActivityVo vo = buildActivity(param, now);
        vo.setActivityId(IDGenerator.nextSnowId());
        vo.setCreatedAt(now);
        vo.setUpdatedAt(now);
        vo.setDiscounts(buildDiscounts(param, vo.getActivityId(), now));
        mongoTemplate.insert(vo, KylinGroupBuyActivityVo.class.getSimpleName());
        sendReplaceActivitySql(vo);
        cacheActivity(vo);
        return ResponseDto.success();
    }

    @Override
    public ResponseDto<KylinGroupBuyActivityVo> update(KylinGroupBuyActivityUpdateParam param) {
        KylinGroupBuyActivityVo old = findActivity(param.getActivityId());
        if (old == null) {
            return ResponseDto.failure("拼团活动不存在");
        }
        ResponseDto<Boolean> validate = validateParam(param);
        if (!validate.isSuccess()) {
            return ResponseDto.failure(validate.getMessage());
        }
        String now = now();
        KylinGroupBuyActivityVo vo = buildActivity(param, now);
        vo.setActivityId(old.getActivityId());
        vo.setStatus(old.getStatus());
        vo.setCreatedAt(old.getCreatedAt());
        vo.setUpdatedAt(now);
        vo.setDiscounts(buildDiscounts(param, old.getActivityId(), now));
        replaceActivity(vo);
        sendReplaceActivitySql(vo);
        return ResponseDto.success();
    }

    @Override
    public ResponseDto<Boolean> delete(String activityId) {
        KylinGroupBuyActivityVo old = findActivity(activityId);
        if (old == null) {
            return ResponseDto.failure("拼团活动不存在");
        }
        if (GroupBuyStatusConst.ActivityStatus.ACTIVE.equals(old.getStatus())) {
            return ResponseDto.failure("进行中的拼团活动不可删除");
        }
        mongoTemplate.remove(Query.query(Criteria.where("activityId").is(activityId)),
                KylinGroupBuyActivityVo.class, KylinGroupBuyActivityVo.class.getSimpleName());
        sendDeleteActivitySql(activityId);
        clearActivityCache(old);
        return ResponseDto.success(true);
    }

    @Override
    public ResponseDto<Boolean> toggle(String activityId, Integer targetStatus) {
        KylinGroupBuyActivityVo old = findActivity(activityId);
        if (old == null) {
            return ResponseDto.failure("拼团活动不存在");
        }
        if (!GroupBuyStatusConst.ActivityStatus.ACTIVE.equals(targetStatus)
                && !GroupBuyStatusConst.ActivityStatus.CLOSED.equals(targetStatus)) {
            return ResponseDto.failure("仅支持启用或关闭活动");
        }
        Integer fromStatus = old.getStatus();
        if (GroupBuyStatusConst.ActivityStatus.ACTIVE.equals(targetStatus)
                && !GroupBuyStatusConst.ActivityStatus.PENDING.equals(fromStatus)
                && !GroupBuyStatusConst.ActivityStatus.CLOSED.equals(fromStatus)) {
            return ResponseDto.failure("仅待启动或已关闭的活动可启用");
        }
        if (GroupBuyStatusConst.ActivityStatus.CLOSED.equals(targetStatus)
                && !GroupBuyStatusConst.ActivityStatus.ACTIVE.equals(fromStatus)) {
            return ResponseDto.failure("仅进行中的活动可关闭");
        }
        old.setStatus(targetStatus);
        old.setUpdatedAt(now());
        replaceActivity(old);
        sendUpdateActivityStatusSql(old.getActivityId(), fromStatus, targetStatus,
                DateUtil.Formatter.yyyyMMddHHmmss.parse(old.getUpdatedAt()));
        return ResponseDto.success(true);
    }

    private static final int EXPORT_MAX_SIZE = 10000;

    @Override
    public ResponseDto<KylinGroupBuyPartnerOrderStatsVo> orderStats() {
        String collection = KylinGroupBuyOrderVo.class.getSimpleName();
        KylinGroupBuyPartnerOrderStatsVo result = new KylinGroupBuyPartnerOrderStatsVo();
        result.setTotal(mongoTemplate.count(Query.query(new Criteria()), collection));
        result.setPending(mongoTemplate.count(
                Query.query(Criteria.where("status").is(GroupBuyStatusConst.OrderStatus.PENDING)), collection));
        result.setSuccess(mongoTemplate.count(
                Query.query(Criteria.where("status").is(GroupBuyStatusConst.OrderStatus.SUCCESS)), collection));
        result.setFailed(mongoTemplate.count(
                Query.query(Criteria.where("status").is(GroupBuyStatusConst.OrderStatus.FAILED)), collection));
        return ResponseDto.success(result);
    }

    @Override
    public ResponseDto<PageInfo<KylinGroupBuyPartnerOrderListVo>> orderList(String keyword, Integer status,
                                                                            Integer page, Integer size) {
        ResponseDto<Boolean> statusCheck = validateOrderListStatus(status);
        if (!statusCheck.isSuccess()) {
            return ResponseDto.failure(statusCheck.getMessage());
        }
        Criteria criteria = buildOrderListCriteria(keyword, status);
        String collection = KylinGroupBuyOrderVo.class.getSimpleName();
        long total = mongoTemplate.count(Query.query(criteria), collection);
        Query dataQuery = Query.query(criteria).with(Sort.by(Sort.Direction.DESC, "createdAt"));
        int pageNo = page == null || page < 1 ? 1 : page;
        int pageSize = size == null || size < 1 ? 20 : size;
        dataQuery.skip((long) (pageNo - 1) * pageSize).limit(pageSize);
        List<KylinGroupBuyOrderVo> orders = mongoTemplate.find(dataQuery, KylinGroupBuyOrderVo.class, collection);
        List<KylinGroupBuyPartnerOrderListVo> list = toPartnerOrderListVos(orders);
        PageInfo<KylinGroupBuyPartnerOrderListVo> pageInfo = new PageInfo<>(list);
        pageInfo.setTotal(total);
        pageInfo.setPageNum(pageNo);
        pageInfo.setPageSize(pageSize);
        return ResponseDto.success(pageInfo);
    }

    @Override
    public ResponseDto<KylinGroupBuyPartnerOrderDetailVo> orderDetail(String groupOrderId) {
        if (!StringUtils.hasText(groupOrderId)) {
            return ResponseDto.failure("拼团订单号不能为空");
        }
        KylinGroupBuyOrderVo order = mongoTemplate.findOne(
                Query.query(Criteria.where("groupOrderId").is(groupOrderId)),
                KylinGroupBuyOrderVo.class, KylinGroupBuyOrderVo.class.getSimpleName());
        if (order == null) {
            return ResponseDto.failure("拼团订单不存在");
        }
        return ResponseDto.success(toPartnerOrderDetailVo(order));
    }

    @Override
    public void exportOrders(String keyword, Integer status, HttpServletResponse response) {
        ResponseDto<Boolean> statusCheck = validateOrderListStatus(status);
        if (!statusCheck.isSuccess()) {
            throw new IllegalArgumentException(statusCheck.getMessage());
        }
        Criteria criteria = buildOrderListCriteria(keyword, status);
        Query query = Query.query(criteria).with(Sort.by(Sort.Direction.DESC, "createdAt")).limit(EXPORT_MAX_SIZE);
        List<KylinGroupBuyOrderVo> orders = mongoTemplate.find(query,
                KylinGroupBuyOrderVo.class, KylinGroupBuyOrderVo.class.getSimpleName());
        List<KylinGroupBuyPartnerOrderListVo> rows = toPartnerOrderListVos(orders);
        try {
            GroupBuyOrderExcelUtil.export(rows, response);
        } catch (Exception e) {
            throw new IllegalStateException("导出拼团订单失败", e);
        }
    }

    private ResponseDto<Boolean> validateOrderListStatus(Integer status) {
        if (status == null) {
            return ResponseDto.success(true);
        }
        if (GroupBuyStatusConst.OrderStatus.PENDING.equals(status)
                || GroupBuyStatusConst.OrderStatus.SUCCESS.equals(status)
                || GroupBuyStatusConst.OrderStatus.FAILED.equals(status)) {
            return ResponseDto.success(true);
        }
        return ResponseDto.failure("状态仅支持：0拼团中、1拼团成功、2拼团失败");
    }

    private Criteria buildOrderListCriteria(String keyword, Integer status) {
        List<Criteria> andList = new ArrayList<>();
        if (status != null) {
            andList.add(Criteria.where("status").is(status));
        }
        if (StringUtils.hasText(keyword)) {
            String escaped = escapeRegex(keyword.trim());
            andList.add(new Criteria().orOperator(
                    Criteria.where("groupOrderId").regex(escaped, "i"),
                    Criteria.where("members.nickname").regex(escaped, "i"),
                    Criteria.where("leaderUserId").regex(escaped, "i")
            ));
        }
        if (andList.isEmpty()) {
            return new Criteria();
        }
        if (andList.size() == 1) {
            return andList.get(0);
        }
        return new Criteria().andOperator(andList.toArray(new Criteria[0]));
    }

    private List<KylinGroupBuyPartnerOrderListVo> toPartnerOrderListVos(List<KylinGroupBuyOrderVo> orders) {
        List<KylinGroupBuyPartnerOrderListVo> result = new ArrayList<>();
        if (CollectionUtils.isEmpty(orders)) {
            return result;
        }
        Map<String, String> activityNameMap = loadActivityNameMap(orders);
        Map<String, BigDecimal> amountMap = loadLeaderAmountMap(orders);
        for (KylinGroupBuyOrderVo order : orders) {
            KylinGroupBuyPartnerOrderListVo vo = new KylinGroupBuyPartnerOrderListVo();
            vo.setGroupOrderId(order.getGroupOrderId());
            vo.setActivityName(activityNameMap.get(order.getActivityId()));
            vo.setLeaderUserId(order.getLeaderUserId());
            KylinGroupBuyMemberVo leader = findLeaderMember(order);
            if (leader != null) {
                vo.setLeaderNickname(leader.getNickname());
                vo.setLeaderAvatar(leader.getAvatar());
            }
            vo.setCurrentCount(order.getCurrentCount());
            vo.setTargetCount(order.getTargetCount());
            vo.setOrderAmount(amountMap.get(order.getGroupOrderId()));
            vo.setCreatedAt(order.getCreatedAt());
            vo.setStatus(order.getStatus());
            result.add(vo);
        }
        return result;
    }

    private KylinGroupBuyPartnerOrderDetailVo toPartnerOrderDetailVo(KylinGroupBuyOrderVo order) {
        KylinGroupBuyPartnerOrderDetailVo vo = new KylinGroupBuyPartnerOrderDetailVo();
        vo.setGroupOrderId(order.getGroupOrderId());
        vo.setActivityId(order.getActivityId());
        KylinGroupBuyActivityVo activity = findActivity(order.getActivityId());
        vo.setActivityName(activity == null ? null : activity.getName());
        vo.setStatus(order.getStatus());
        vo.setOrderAmount(resolveLeaderAmount(order));
        vo.setCreatedAt(order.getCreatedAt());
        vo.setCurrentCount(order.getCurrentCount());
        vo.setTargetCount(order.getTargetCount());
        vo.setLeaderUserId(order.getLeaderUserId());

        List<KylinGroupBuyPartnerOrderMemberVo> members = new ArrayList<>();
        if (!CollectionUtils.isEmpty(order.getMembers())) {
            for (KylinGroupBuyMemberVo m : order.getMembers()) {
                KylinGroupBuyPartnerOrderMemberVo item = new KylinGroupBuyPartnerOrderMemberVo();
                item.setUserId(m.getUserId());
                item.setNickname(m.getNickname());
                item.setAvatar(m.getAvatar());
                item.setStatus(m.getStatus());
                item.setIsRobot(m.getIsRobot());
                boolean isLeader = order.getLeaderUserId() != null && order.getLeaderUserId().equals(m.getUserId());
                item.setIsLeader(isLeader ? 1 : 0);
                members.add(item);
            }
        }
        vo.setMembers(members);

        KylinGroupBuyMemberVo leader = findLeaderMember(order);
        if (leader != null) {
            vo.setLeaderNickname(leader.getNickname());
            vo.setLeaderAvatar(leader.getAvatar());
        }
        return vo;
    }

    private Map<String, String> loadActivityNameMap(List<KylinGroupBuyOrderVo> orders) {
        Set<String> activityIds = new HashSet<>();
        for (KylinGroupBuyOrderVo order : orders) {
            if (StringUtils.hasText(order.getActivityId())) {
                activityIds.add(order.getActivityId());
            }
        }
        Map<String, String> map = new HashMap<>();
        if (activityIds.isEmpty()) {
            return map;
        }
        List<KylinGroupBuyActivityVo> activities = mongoTemplate.find(
                Query.query(Criteria.where("activityId").in(activityIds)),
                KylinGroupBuyActivityVo.class, KylinGroupBuyActivityVo.class.getSimpleName());
        for (KylinGroupBuyActivityVo activity : activities) {
            map.put(activity.getActivityId(), activity.getName());
        }
        return map;
    }

    private Map<String, BigDecimal> loadLeaderAmountMap(List<KylinGroupBuyOrderVo> orders) {
        Map<String, String> groupToTicket = new HashMap<>();
        Set<String> ticketIds = new HashSet<>();
        for (KylinGroupBuyOrderVo order : orders) {
            String ticketId = resolveLeaderOrderTicketsId(order);
            if (StringUtils.hasText(ticketId)) {
                groupToTicket.put(order.getGroupOrderId(), ticketId);
                ticketIds.add(ticketId);
            }
        }
        Map<String, BigDecimal> ticketAmount = new HashMap<>();
        if (!ticketIds.isEmpty()) {
            List<KylinOrderTicketVo> tickets = mongoTemplate.find(
                    Query.query(Criteria.where("orderTicketsId").in(ticketIds)),
                    KylinOrderTicketVo.class, KylinOrderTicketVo.class.getSimpleName());
            for (KylinOrderTicketVo ticket : tickets) {
                ticketAmount.put(ticket.getOrderTicketsId(), ticket.getPriceActual());
            }
            for (String ticketId : ticketIds) {
                if (!ticketAmount.containsKey(ticketId)) {
                    Object cached = redisUtil.get(KylinRedisConst.ORDER.concat(ticketId));
                    if (cached instanceof KylinOrderTicketVo) {
                        ticketAmount.put(ticketId, ((KylinOrderTicketVo) cached).getPriceActual());
                    }
                }
            }
        }
        Map<String, BigDecimal> result = new HashMap<>();
        for (Map.Entry<String, String> e : groupToTicket.entrySet()) {
            result.put(e.getKey(), ticketAmount.get(e.getValue()));
        }
        return result;
    }

    private BigDecimal resolveLeaderAmount(KylinGroupBuyOrderVo order) {
        String ticketId = resolveLeaderOrderTicketsId(order);
        if (!StringUtils.hasText(ticketId)) {
            return null;
        }
        Object cached = redisUtil.get(KylinRedisConst.ORDER.concat(ticketId));
        if (cached instanceof KylinOrderTicketVo) {
            return ((KylinOrderTicketVo) cached).getPriceActual();
        }
        KylinOrderTicketVo ticket = mongoTemplate.findOne(
                Query.query(Criteria.where("orderTicketsId").is(ticketId)),
                KylinOrderTicketVo.class, KylinOrderTicketVo.class.getSimpleName());
        return ticket == null ? null : ticket.getPriceActual();
    }

    private String resolveLeaderOrderTicketsId(KylinGroupBuyOrderVo order) {
        if (order == null) {
            return null;
        }
        if (StringUtils.hasText(order.getOrderTicketsId())) {
            return order.getOrderTicketsId();
        }
        KylinGroupBuyMemberVo leader = findLeaderMember(order);
        return leader == null ? null : leader.getOrderTicketsId();
    }

    private KylinGroupBuyMemberVo findLeaderMember(KylinGroupBuyOrderVo order) {
        if (order == null || !StringUtils.hasText(order.getLeaderUserId())
                || CollectionUtils.isEmpty(order.getMembers())) {
            return null;
        }
        for (KylinGroupBuyMemberVo m : order.getMembers()) {
            if (order.getLeaderUserId().equals(m.getUserId())) {
                return m;
            }
        }
        return null;
    }

    /** Mongo $regex 不支持 \\Q..\\E，手工转义特殊字符。 */
    private static String escapeRegex(String input) {
        return Pattern.compile("([\\\\.*+?^${}()|\\[\\]])").matcher(input).replaceAll("\\\\$1");
    }

    private ResponseDto<Boolean> validateParam(KylinGroupBuyActivityCreateParam param) {
        LocalDateTime startTime;
        LocalDateTime endTime;
        try {
            startTime = DateUtil.Formatter.yyyyMMddHHmmss.parse(param.getStartTime());
            endTime = DateUtil.Formatter.yyyyMMddHHmmss.parse(param.getEndTime());
        } catch (Exception e) {
            return ResponseDto.failure("开始/结束时间格式错误，需为 yyyy-MM-dd HH:mm:ss");
        }
        if (endTime.isBefore(startTime) || endTime.isEqual(startTime)) {
            return ResponseDto.failure("活动结束时间必须晚于开始时间");
        }
        if (GroupBuyStatusConst.LaunchType.SCHEDULED.equals(param.getLaunchType())) {
            if (param.getScheduledTime() == null || param.getScheduledTime().trim().isEmpty()) {
                return ResponseDto.failure("定时启动必须填写启动时间");
            }
            try {
                DateUtil.Formatter.yyyyMMddHHmmss.parse(param.getScheduledTime());
            } catch (Exception e) {
                return ResponseDto.failure("定时启动时间格式错误，需为 yyyy-MM-dd HH:mm:ss");
            }
        }
        KylinPerformanceVo performanceVo = redisSlimeUtils.getPerformanceVo(param.getPerformanceId());
        if (performanceVo == null || CollectionUtils.isEmpty(performanceVo.getTicketTimeList())) {
            return ResponseDto.failure("演出不存在");
        }
        if (param.getMinGroupSize() == null || param.getMinGroupSize() < 2) {
            return ResponseDto.failure("成团人数不能小于2");
        }
        if (param.getMaxBuyPerUser() == null || param.getMaxBuyPerUser() < 1) {
            return ResponseDto.failure("限购数量不能小于1");
        }
        if (!CollectionUtils.isEmpty(param.getDiscounts())) {
            for (KylinGroupBuyActivityCreateParam.DiscountParam discount : param.getDiscounts()) {
                if (!ticketExists(performanceVo, discount.getTimeId(), discount.getTicketId())) {
                    return ResponseDto.failure("优惠配置的场次或票档不存在");
                }
                if (discount.getDiscountValue() == null || discount.getDiscountValue().compareTo(BigDecimal.ZERO) <= 0) {
                    return ResponseDto.failure("优惠值必须大于0");
                }
            }
        }
        return ResponseDto.success(true);
    }

    private boolean ticketExists(KylinPerformanceVo performanceVo, String timeId, String ticketId) {
        for (KylinTicketTimesVo timeVo : performanceVo.getTicketTimeList()) {
            if (!timeId.equals(timeVo.getTimeId()) || CollectionUtils.isEmpty(timeVo.getTicketList())) {
                continue;
            }
            for (KylinTicketVo ticketVo : timeVo.getTicketList()) {
                if (ticketId.equals(ticketVo.getTicketsId())) {
                    return true;
                }
            }
        }
        return false;
    }

    private KylinGroupBuyActivityVo buildActivity(KylinGroupBuyActivityCreateParam param, String now) {
        KylinGroupBuyActivityVo vo = new KylinGroupBuyActivityVo();
        vo.setName(param.getName());
        vo.setPerformanceId(param.getPerformanceId());
        vo.setMinGroupSize(param.getMinGroupSize());
        vo.setMaxBuyPerUser(param.getMaxBuyPerUser());
        vo.setStartTime(param.getStartTime());
        vo.setEndTime(param.getEndTime());
        vo.setLaunchType(param.getLaunchType());
        if (param.getScheduledTime() != null && !param.getScheduledTime().trim().isEmpty()) {
            vo.setScheduledTime(param.getScheduledTime());
        }
        vo.setDescription(param.getDescription());
        if (GroupBuyStatusConst.LaunchType.IMMEDIATE.equals(param.getLaunchType())) {
            vo.setStatus(GroupBuyStatusConst.ActivityStatus.ACTIVE);
        } else {
            vo.setStatus(GroupBuyStatusConst.ActivityStatus.PENDING);
        }
        vo.setUpdatedAt(now);
        return vo;
    }

    private List<KylinGroupBuyDiscountVo> buildDiscounts(KylinGroupBuyActivityCreateParam param, String activityId, String now) {
        List<KylinGroupBuyDiscountVo> list = new ArrayList<>();
        for (KylinGroupBuyActivityCreateParam.DiscountParam discountParam : param.getDiscounts()) {
            KylinGroupBuyDiscountVo vo = new KylinGroupBuyDiscountVo();
            vo.setDiscountId(IDGenerator.nextSnowId());
            vo.setActivityId(activityId);
            vo.setPerformanceId(param.getPerformanceId());
            vo.setTimeId(discountParam.getTimeId());
            vo.setTicketId(discountParam.getTicketId());
            vo.setDiscountType(discountParam.getDiscountType());
            vo.setDiscountValue(discountParam.getDiscountValue());
            vo.setCreatedAt(now);
            list.add(vo);
        }
        return list;
    }

    private KylinGroupBuyActivityVo findActivity(String activityId) {
        Object obj = redisUtil.get(KylinRedisConst.GROUP_BUY_ACTIVITY.concat(activityId));
        if (obj != null) {
            return (KylinGroupBuyActivityVo) obj;
        }
        KylinGroupBuyActivityVo activity = mongoTemplate.findOne(Query.query(Criteria.where("activityId").is(activityId)),
                KylinGroupBuyActivityVo.class, KylinGroupBuyActivityVo.class.getSimpleName());
        cacheActivity(activity);
        return activity;
    }

    private void replaceActivity(KylinGroupBuyActivityVo activity) {
        clearActivityCache(activity);
        Query query = Query.query(Criteria.where("activityId").is(activity.getActivityId()));
        BasicDBObject updateObj = ObjectUtil.cloneBasicDBObject()
                .append("$set", mongoTemplate.getConverter().convertToMongoType(activity));
        mongoTemplate.getCollection(KylinGroupBuyActivityVo.class.getSimpleName())
                .updateOne(query.getQueryObject(), updateObj);
        cacheActivity(activity);
    }

    private void sendReplaceActivitySql(KylinGroupBuyActivityVo activity) {
        LinkedList<String> sqls = CollectionUtil.linkedListString();
        LinkedList<Object[]> activityDel = CollectionUtil.linkedListObjectArr();
        LinkedList<Object[]> activityInsert = CollectionUtil.linkedListObjectArr();
        LinkedList<Object[]> discountDel = CollectionUtil.linkedListObjectArr();
        LinkedList<Object[]> discountInsert = CollectionUtil.linkedListObjectArr();

        sqls.add(SqlMapping.get("kylin_group_buy_activity.delete"));
        activityDel.add(new Object[]{activity.getActivityId()});
        sqls.add(SqlMapping.get("kylin_group_buy_activity.insert"));
        activityInsert.add(activityInsertObject(activity));
        sqls.add(SqlMapping.get("kylin_group_buy_discount.delete_by_activity"));
        discountDel.add(new Object[]{activity.getActivityId()});
        if (!CollectionUtils.isEmpty(activity.getDiscounts())) {
            sqls.add(SqlMapping.get("kylin_group_buy_discount.insert"));
            for (KylinGroupBuyDiscountVo discount : activity.getDiscounts()) {
                discountInsert.add(discountInsertObject(discount));
            }
            queueUtil.sendMsgByRedis(MQConst.SlimeQueue.SQL_SLIME_FIELD.getKey(),
                    SqlMapping.gets(sqls, activityDel, activityInsert, discountDel, discountInsert));
        } else {
            queueUtil.sendMsgByRedis(MQConst.SlimeQueue.SQL_SLIME_FIELD.getKey(),
                    SqlMapping.gets(sqls, activityDel, activityInsert, discountDel));
        }
    }

    private void sendDeleteActivitySql(String activityId) {
        LinkedList<String> sqls = CollectionUtil.linkedListString();
        LinkedList<Object[]> activityDel = CollectionUtil.linkedListObjectArr();
        LinkedList<Object[]> discountDel = CollectionUtil.linkedListObjectArr();
        sqls.add(SqlMapping.get("kylin_group_buy_activity.delete"));
        activityDel.add(new Object[]{activityId});
        sqls.add(SqlMapping.get("kylin_group_buy_discount.delete_by_activity"));
        discountDel.add(new Object[]{activityId});
        queueUtil.sendMsgByRedis(MQConst.SlimeQueue.SQL_SLIME_FIELD.getKey(),
                SqlMapping.gets(sqls, activityDel, discountDel));
    }

    private void sendUpdateActivityStatusSql(String activityId, Integer fromStatus, Integer toStatus, LocalDateTime updatedAt) {
        queueUtil.sendMsgByRedis(MQConst.SlimeQueue.SQL_SLIME_FIELD.getKey(),
                SqlMapping.get("kylin_group_buy_activity.update_status",
                        toStatus, updatedAt, activityId, fromStatus));
    }

    private Object[] activityInsertObject(KylinGroupBuyActivityVo vo) {
        return new Object[]{
                vo.getActivityId(), vo.getName(), vo.getPerformanceId(), vo.getMinGroupSize(), vo.getMaxBuyPerUser(),
                DateUtil.Formatter.yyyyMMddHHmmss.parse(vo.getStartTime()),
                DateUtil.Formatter.yyyyMMddHHmmss.parse(vo.getEndTime()),
                vo.getLaunchType(),
                vo.getScheduledTime() == null ? null : DateUtil.Formatter.yyyyMMddHHmmss.parse(vo.getScheduledTime()),
                vo.getStatus(), vo.getDescription(),
                DateUtil.Formatter.yyyyMMddHHmmss.parse(vo.getCreatedAt()),
                DateUtil.Formatter.yyyyMMddHHmmss.parse(vo.getUpdatedAt())
        };
    }

    private Object[] discountInsertObject(KylinGroupBuyDiscountVo vo) {
        return new Object[]{
                vo.getDiscountId(), vo.getActivityId(), vo.getPerformanceId(), vo.getTimeId(), vo.getTicketId(),
                vo.getDiscountType(), vo.getDiscountValue(), DateUtil.Formatter.yyyyMMddHHmmss.parse(vo.getCreatedAt())
        };
    }

    private void cacheActivity(KylinGroupBuyActivityVo activity) {
        if (activity == null) {
            return;
        }
        redisUtil.set(KylinRedisConst.GROUP_BUY_ACTIVITY.concat(activity.getActivityId()), activity, RedisKeyExpireConst.KYLIN_GROUP_BUY_EXPIRE);
    }

    private void clearActivityCache(KylinGroupBuyActivityVo activity) {
        if (activity == null) {
            return;
        }
        redisUtil.del(KylinRedisConst.GROUP_BUY_ACTIVITY.concat(activity.getActivityId()));
    }

    private String now() {
        return DateUtil.Formatter.yyyyMMddHHmmss.format(LocalDateTime.now());
    }
}
