package cn.iocoder.yudao.module.member.service.activity;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.member.controller.admin.activity.vo.ActivityPageReqVO;
import cn.iocoder.yudao.module.member.controller.admin.activity.vo.ActivityRespVO;
import cn.iocoder.yudao.module.member.controller.admin.activity.vo.ActivitySaveReqVO;
import cn.iocoder.yudao.module.member.controller.admin.activityawards.vo.ActivityAwardsListDTO;
import cn.iocoder.yudao.module.member.controller.admin.activityawardsuser.vo.ActivityAwardsUserCountVO;
import cn.iocoder.yudao.module.member.controller.admin.activitypay.vo.ActivityPayCountVO;
import cn.iocoder.yudao.module.member.controller.admin.activityrestaurant.vo.ActivityRestaurantRespVO;
import cn.iocoder.yudao.module.member.controller.admin.activityrestaurant.vo.ActivityRestaurantSaveDto;
import cn.iocoder.yudao.module.member.controller.admin.activityrestaurant.vo.ActivityRestaurantSaveReqVO;
import cn.iocoder.yudao.module.member.controller.admin.activitytable.vo.ActivityTableCountVO;
import cn.iocoder.yudao.module.member.controller.admin.activityuser.vo.ActivityUserCountVO;
import cn.iocoder.yudao.module.member.controller.app.activity.vo.AppActivityPrizeDrawVO;
import cn.iocoder.yudao.module.member.controller.app.activity.vo.AppActivityRespVO;
import cn.iocoder.yudao.module.member.dal.dataobject.activity.ActivityDO;
import cn.iocoder.yudao.module.member.dal.dataobject.activityawards.ActivityAwardsDO;
import cn.iocoder.yudao.module.member.dal.dataobject.activityawardsuser.ActivityAwardsUserDO;
import cn.iocoder.yudao.module.member.dal.dataobject.activityrestaurant.ActivityRestaurantDO;
import cn.iocoder.yudao.module.member.dal.dataobject.activityuser.ActivityUserDO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import cn.iocoder.yudao.module.member.dal.mysql.activity.ActivityMapper;
import cn.iocoder.yudao.module.member.service.activityawards.ActivityAwardsService;
import cn.iocoder.yudao.module.member.service.activityawardsuser.ActivityAwardsUserService;
import cn.iocoder.yudao.module.member.service.activitypay.ActivityPayService;
import cn.iocoder.yudao.module.member.service.activityrestaurant.ActivityRestaurantService;
import cn.iocoder.yudao.module.member.service.activitytable.ActivityTableService;
import cn.iocoder.yudao.module.member.service.activitytabledish.ActivityTableDishService;
import cn.iocoder.yudao.module.member.service.activityuser.ActivityUserService;
import cn.iocoder.yudao.module.member.service.user.MemberUserService;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*;

/**
 * 活动 Service 实现类
 *
 * @author 我是秦俊旗
 */
@Service
@Validated
public class ActivityServiceImpl implements ActivityService {

    @Resource
    private ActivityMapper activityMapper;

    @Resource
    @Lazy
    private ActivityAwardsService activityAwardsService;

    @Resource
    @Lazy
    private ActivityUserService activityUserService;

    @Resource
    @Lazy
    private ActivityTableService activityTableService;

    @Resource
    @Lazy
    private ActivityRestaurantService activityRestaurantService;

    @Resource
    @Lazy
    private ActivityAwardsUserService activityAwardsUserService;

    @Resource
    private MemberUserService memberUserService;

    @Resource
    @Lazy
    private ActivityPayService activityPayService;
    @Resource
    @Lazy
    private ActivityTableDishService activityTableDishService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createActivity(ActivitySaveReqVO createReqVO) {
        // 插入
        ActivityDO activity = BeanUtils.toBean(createReqVO, ActivityDO.class);
        activityMapper.insert(activity);
        ArrayList<ActivityAwardsDO> activityAwardsDOarrayList = new ArrayList<ActivityAwardsDO>();
        for (ActivityAwardsListDTO dto : createReqVO.getActivityAwardsList()) {
            ActivityAwardsDO activityAwardsDO = BeanUtils.toBean(dto, ActivityAwardsDO.class);
            activityAwardsDO.setActivityId(activity.getId());
            activityAwardsDOarrayList.add(activityAwardsDO);
        }
        if (CollectionUtil.isNotEmpty(activityAwardsDOarrayList)) {
            activityAwardsService.insertActivityAwards(activityAwardsDOarrayList);
        }
        if ("1".equals(createReqVO.getReservationStatus()) && createReqVO.getActivityRestaurantSaveDto() != null) {

            ActivityRestaurantSaveReqVO bean = BeanUtils.toBean(createReqVO.getActivityRestaurantSaveDto(), ActivityRestaurantSaveReqVO.class);
            if (bean.getTotalTable() <= 0) {
                throw exception(ACTIVITY_TABLE_ZERO);
            }
            bean.setActivityId(activity.getId());
            activityRestaurantService.createActivityRestaurant(bean);
            activityTableDishService.createByRestaurant(bean);
        }

        // 返回
        return activity.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateActivity(ActivitySaveReqVO updateReqVO) {
        // 校验存在
        validateActivityExists(updateReqVO.getId());
        validateActivityTime(updateReqVO.getId());
        // 更新
        ActivityDO updateObj = BeanUtils.toBean(updateReqVO, ActivityDO.class);

        if ("1".equals(updateReqVO.getReservationStatus()) && updateReqVO.getActivityRestaurantSaveDto() != null) {

            ActivityRestaurantSaveDto activityRestaurantSaveDto = updateReqVO.getActivityRestaurantSaveDto();
            ActivityRestaurantSaveReqVO bean = BeanUtils.toBean(activityRestaurantSaveDto, ActivityRestaurantSaveReqVO.class);
            if (bean.getTotalTable() <= 0) {
                throw exception(ACTIVITY_TABLE_ZERO);
            }
            bean.setActivityId(updateObj.getId());
            if (bean.getId() != null) {
//                activityTableService.deleteByActivityId(updateObj.getId());
                activityRestaurantService.updateActivityRestaurant(bean);
            } else {
                bean.setActivityId(updateObj.getId());
                activityRestaurantService.createActivityRestaurant(bean);
                activityTableDishService.createByRestaurant(bean);
            }
        }
        activityMapper.updateById(updateObj);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteActivity(Long id) {
        // 校验存在
        validateActivityExists(id);
        validateActivityTime(id);
        // 删除
        activityMapper.deleteById(id);
        activityAwardsService.deleteByActivityId(id);
        activityRestaurantService.deleteByActivityId(id);

    }

    private void validateActivityExists(Long id) {
        if (activityMapper.selectById(id) == null) {
            throw exception(ACTIVITY_NOT_EXISTS);
        }
    }

    private void validateActivityTime(Long id) {
        ActivityDO activityDO = activityMapper.selectById(id);
        LocalDateTime startTime = activityDO.getStartTime();
        LocalDateTime endTime = activityDO.getEndTime();
        //现在时间再开始时间之后
        if (startTime.isBefore(LocalDateTime.now())) {
            throw exception(ACTIVITY_START);
        }

    }

    @Override
    public ActivityDO getActivity(Long id) {
        return activityMapper.selectById(id);
    }

    @Override
    public PageResult<ActivityRespVO> getActivityPage(ActivityPageReqVO pageReqVO) {
        PageResult<ActivityDO>  activityDOPageResult = activityMapper.selectPage(pageReqVO);
        List<ActivityDO> list = activityDOPageResult.getList();
        List<Long> activityIds = list.stream().map(ActivityDO::getId).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(activityIds)) {
            return BeanUtils.toBean(activityDOPageResult, ActivityRespVO.class);
        }
        Map<Long, ActivityUserCountVO> userCountVOMap = activityUserService.countByActivityIds(activityIds);
        Map<Long, ActivityTableCountVO> tableCountVOMap = activityTableService.countByActivityIds(activityIds);
        Map<Long, ActivityAwardsUserCountVO> awardsUserCountVOMap = activityAwardsUserService.countByActivityIds(activityIds);
        Map<Long, ActivityPayCountVO> longActivityPayCountVOMap = activityPayService.countByActivityIds(activityIds);
        ArrayList<ActivityRespVO> activityRespVOS = new ArrayList<>();

        for (ActivityDO activity : list) {
            ActivityRespVO bean = BeanUtils.toBean(activity, ActivityRespVO.class);
            bean.setApplyNum(userCountVOMap.getOrDefault(activity.getId(), new ActivityUserCountVO()).getApplyNum());
            bean.setReserveNum(tableCountVOMap.getOrDefault(activity.getId(), new ActivityTableCountVO()).getReserveNum());
            bean.setIncome((longActivityPayCountVOMap.getOrDefault(activity.getId(), new ActivityPayCountVO()).getIncome()));
            bean.setAwardsNum(awardsUserCountVOMap.getOrDefault(activity.getId(), new ActivityAwardsUserCountVO()).getAwardsNum());
            activityRespVOS.add(bean);
        }
        return new PageResult<>(activityRespVOS, activityDOPageResult.getTotal());
    }

    @Override
    public PageResult<ActivityRespVO> getUserActivityPage(ActivityPageReqVO pageReqVO) {
        pageReqVO.setUserId(SecurityFrameworkUtils.getLoginUserId());
        PageResult<ActivityDO>  activityDOPageResult = activityMapper.selectPageApp(pageReqVO);
        List<ActivityDO> list = activityDOPageResult.getList();
        List<Long> activityIds = list.stream().map(ActivityDO::getId).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(activityIds)) {
            return BeanUtils.toBean(activityDOPageResult, ActivityRespVO.class);
        }
        Map<Long, ActivityUserCountVO> userCountVOMap = activityUserService.countByActivityIds(activityIds);
        List<Long> applyByActivityIds = activityUserService.isApplyByActivityIds(activityIds, pageReqVO.getUserId());

        ArrayList<ActivityRespVO> activityRespVOS = new ArrayList<>();
        for (ActivityDO activity : list) {
            ActivityRespVO bean = BeanUtils.toBean(activity, ActivityRespVO.class);
            bean.setApplyNum(userCountVOMap.getOrDefault(activity.getId(), new ActivityUserCountVO()).getApplyNum());
            bean.setIsApply(applyByActivityIds.contains(activity.getId()));
            activityRespVOS.add(bean);
        }
        return new PageResult<>(activityRespVOS, activityDOPageResult.getTotal());
    }

    @Override
    public List<ActivityAwardsUserDO> prizeDraw(AppActivityPrizeDrawVO drawVO) {

        //  查询奖品
        ActivityAwardsDO activityAwards = activityAwardsService.getActivityAwards(drawVO.getAwardsId());

        // 查询活动
        ActivityDO activityDO = activityMapper.selectById(activityAwards.getActivityId());

        if(activityDO.getStartTime().isAfter(LocalDateTime.now())){
            throw exception(new ErrorCode(1_005_000_10, "活动未开始"));
        }

        // 查询报名用户
        List<ActivityUserDO> activityUserDOList = activityUserService.getUserListByActivityId(activityDO.getId());

        //抽奖用户池
        Set<Long> drawUserPool = activityUserDOList.stream().map(ActivityUserDO::getUserId).collect(Collectors.toSet());

        if(CollectionUtil.isEmpty(drawUserPool)){
            throw exception(ACTIVITY_NO_USER);
        }

        // 查询当天中奖用户
        List<ActivityAwardsUserDO> activityAwardsUserDOList = activityAwardsUserService.getAwardsUserListByActivityId(activityDO.getId());
        Set<Long> collect = activityAwardsUserDOList.stream().map(ActivityAwardsUserDO::getUserId).collect(Collectors.toSet());
        drawUserPool.removeAll(collect);
        //统计已中该奖项的人数
//        int count = (int) activityAwardsUserDOList.stream().filter(vo -> vo.getAwardsId().equals(activityAwards.getId())).count();
//
//        if (count + drawVO.getNum() > activityAwards.getAwardsNum()) {
//            throw exception(ACTIVITY_AWARDS_EXCEED);
//        }

//        //查询整个活动中限制类型为整个活动（1）的奖项
//        List<ActivityAwardsDO> limitTypeList = activityAwardsService.getListByActivityIdAndAwardsStatus(activityDO.getId(), "1");
//        //优先排除这批中奖人员
//        if(CollectionUtil.isNotEmpty(limitTypeList)){
//            for (ActivityAwardsDO activityAwardsDO : limitTypeList) {
//                Map<Long, Long> map = activityAwardsUserDOList.stream().filter(vo -> vo.getAwardsId().equals(activityAwardsDO.getId()))
//                        .collect(Collectors.groupingBy(
//                                ActivityAwardsUserDO::getUserId,
//                                Collectors.counting()
//                        ));
//                Integer awardsNum = activityAwardsDO.getAwardsNum();
//                // 筛选出次数 大于限定次数 的用户ID，存入Set去重
//                Set<Long> inconsistentUserList = map.entrySet().stream()
//                        .filter(entry -> entry.getValue() >= awardsNum)
//                        .map(Map.Entry::getKey)
//                        .collect(Collectors.toSet());
//                //  从抽奖用户池中移除次数大于限定次数的用户
//                drawUserPool.removeAll(inconsistentUserList);
//            }
//        }
//
//        // 获取该奖项的限制类型
//        String limitType = activityAwards.getLimitType();
//
//        //如果是单个奖项（2）的话，需要排除该奖项次数满的 整个活动（1）的话上面已排除 无限制（3）无需排除
//        if ("2".equals(limitType)){
//            Map<Long, Long> map = activityAwardsUserDOList.stream().filter(vo -> vo.getAwardsId().equals(activityAwards.getId()))
//                    .collect(Collectors.groupingBy(
//                            ActivityAwardsUserDO::getUserId,
//                            Collectors.counting()
//                    ));
//
//            //每个人可中奖次数
//            Integer awardsNum = activityAwards.getAwardsNum();
//
//            // 筛选出次数 大于限定次数 的用户ID，存入Set去重
//            Set<Long> inconsistentUserList = map.entrySet().stream()
//                    .filter(entry -> entry.getValue() >= awardsNum)
//                    .map(Map.Entry::getKey)
//                    .collect(Collectors.toSet());
//            drawUserPool.removeAll(inconsistentUserList);
//        }

        //开始抽奖
        List<Long> longs = selectRandom(drawUserPool, drawVO.getNum());

        //中奖人员信息
        List<MemberUserDO> userList = memberUserService.getUserList(longs);

        //插入数据库
        ArrayList<ActivityAwardsUserDO> list = new ArrayList<>();
        for (MemberUserDO user : userList) {
            ActivityAwardsUserDO activityAwardsUserDO = new ActivityAwardsUserDO();
            activityAwardsUserDO.setActivityId(activityDO.getId());
            activityAwardsUserDO.setUserId(user.getId());
            activityAwardsUserDO.setNickName(user.getNickname());
            activityAwardsUserDO.setAvatar(user.getAvatar());
            activityAwardsUserDO.setMobile(user.getMobile());
            activityAwardsUserDO.setAwardsId(activityAwards.getId());
            activityAwardsUserDO.setAwardsStatus("0");
            activityAwardsUserDO.setPrize(activityAwards.getPrize());
            activityAwardsUserDO.setAwardsName(activityAwards.getAwardsName());
            list.add(activityAwardsUserDO);
        }
        activityAwardsUserService.insertBatch(list);
        return  list;
    }

    public static List<Long> selectRandom(Set<Long> drawUserPool, int count) {
        // 校验输入
        if (drawUserPool == null || drawUserPool.isEmpty()) {
            return Collections.emptyList();
        }
        int total = drawUserPool.size();
        if (count >= total) {
            return new ArrayList<>(drawUserPool); // 直接返回所有元素
        }

        // 转换为列表并复制，避免修改原始集合
        List<Long> list = new ArrayList<>(drawUserPool);
        Random random = new Random();

        // Fisher-Yates 洗牌算法（原地打乱）
        for (int i = total - 1; i > 0; i--) {
            int j = random.nextInt(i + 1); // 生成 [0, i] 之间的随机索引
            Collections.swap(list, i, j); // 交换元素，确保每个位置等概率出现
        }

        // 取前 count 个元素
        return list.subList(0, count);
    }


    @Override
    public AppActivityRespVO getUserActivity(Long id,Long userId) {
        ActivityDO activityDO = activityMapper.selectById(id);
        if( activityDO == null){
            throw exception(ACTIVITY_NOT_EXISTS);
        }
        AppActivityRespVO activity = BeanUtil.toBean(activityDO, AppActivityRespVO.class);
        Map<Long, ActivityUserCountVO> userCountVOMap = activityUserService.countByActivityIds(Collections.singletonList(id));
        activity.setApplyNum(userCountVOMap.getOrDefault(activity.getId(), new ActivityUserCountVO()).getApplyNum());

        activity.setAwards(activityAwardsUserService.getListByActivityIdAndUserId(id, userId));
        return activity;
    }

    @Override
    public AppActivityRespVO getUserActivityApplyInfo(Long id, Long userId) {
        ActivityDO activityDO = activityMapper.selectById(id);
        if( activityDO == null){
            throw exception(ACTIVITY_NOT_EXISTS);
        }
        AppActivityRespVO activity = BeanUtil.toBean(activityDO, AppActivityRespVO.class);
        Map<Long, ActivityUserCountVO> userCountVOMap = activityUserService.countByActivityIds(Collections.singletonList(id));
        activity.setApplyNum(userCountVOMap.getOrDefault(activity.getId(), new ActivityUserCountVO()).getApplyNum());

        activity.setIsApply(activityUserService.selectIsApply(id, userId));

        activity.setAwardList(activityAwardsService.getListByActivityId(id));
        return activity;
    }

    @Override
    public List<AppActivityRespVO> getReservatList() {
        LocalDateTime now = LocalDateTime.now();
        List<ActivityDO> activityDOS = activityMapper.selectList(Wrappers.<ActivityDO>lambdaQuery()
                .eq(ActivityDO::getReservationStatus, "1")
                .ge(ActivityDO::getEndTime, now)
//                .le(ActivityDO::getStartTime, now)
        );
        List<AppActivityRespVO> result = new ArrayList<>();
        for  (ActivityDO activityDO : activityDOS) {
            AppActivityRespVO bean = BeanUtils.toBean(activityDO, AppActivityRespVO.class);
            ActivityRestaurantDO activityRestaurantDO = activityRestaurantService.getByActivityId(activityDO.getId());
            bean.setActivityRestaurantSaveDto(BeanUtils.toBean(activityRestaurantDO, ActivityRestaurantRespVO.class));
            result.add(bean);
        }
        return result;
    }

    @Override
    public PageResult<ActivityRespVO> getActivityReservePage(ActivityPageReqVO reqVO) {
        PageResult<ActivityDO> activityDOPageResult = activityMapper.selectPage(reqVO, new LambdaQueryWrapperX<ActivityDO>()
                .eq(ActivityDO::getReservationStatus, "1")
                .ge(ActivityDO::getEndTime, LocalDateTime.now())
                .orderByDesc(ActivityDO::getId));
        return BeanUtils.toBean(activityDOPageResult, ActivityRespVO.class);
    }
}

