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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
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.user.vo.MemberUserAddVO;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserListVO;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserResetPasswordReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateMobileByWeixinReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateMobileReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdatePasswordReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppNutritionDayVo;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppNutritionWeekVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppWeekVO;
import cn.iocoder.yudao.module.member.convert.auth.AuthConvert;
import cn.iocoder.yudao.module.member.convert.user.MemberUserConvert;
import cn.iocoder.yudao.module.member.dal.dataobject.diningplates.DiningPlatesDO;
import cn.iocoder.yudao.module.member.dal.dataobject.order.DishOrderDO;
import cn.iocoder.yudao.module.member.dal.dataobject.orderdetail.OrderDetailDO;
import cn.iocoder.yudao.module.member.dal.dataobject.tag.MemberTagDO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import cn.iocoder.yudao.module.member.dal.mysql.diningplates.DiningPlatesMapper;
import cn.iocoder.yudao.module.member.dal.mysql.group.MemberGroupMapper;
import cn.iocoder.yudao.module.member.dal.mysql.order.DishOrderMapper;
import cn.iocoder.yudao.module.member.dal.mysql.orderdetail.OrderDetailMapper;
import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
import cn.iocoder.yudao.module.member.enums.TimePeriodEnum;
import cn.iocoder.yudao.module.member.mq.producer.user.MemberUserProducer;
import cn.iocoder.yudao.module.member.service.admincard.AdminCardService;
import cn.iocoder.yudao.module.member.service.card.CardService;
import cn.iocoder.yudao.module.member.service.order.OrderService;
import cn.iocoder.yudao.module.member.service.orderdetail.OrderDetailService;
import cn.iocoder.yudao.module.member.service.tag.MemberTagService;
import cn.iocoder.yudao.module.member.service.userexpand.UserExpandService;
import cn.iocoder.yudao.module.member.util.MemberConstants;
import cn.iocoder.yudao.module.system.api.dishesnutrition.DishesNutritionApi;
import cn.iocoder.yudao.module.system.api.dishesnutrition.dto.DishesNutritionRespDTO;
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Case;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.WeekFields;
import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*;

/**
 * 会员 User Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Valid
@Slf4j
public class MemberUserServiceImpl implements MemberUserService {
    @Resource
    private DishOrderMapper dishOrderMapper;
    @Resource
    private OrderDetailMapper orderDetailMapper;
    @Resource
    private DishesNutritionApi dishesNutritionApil;
    @Resource
    private DiningPlatesMapper diningPlatesMapper;
    @Resource
    private MemberUserMapper memberUserMapper;
    @Resource
    private CardService cardService;
    @Resource
    private SmsCodeApi smsCodeApi;

    @Resource
    private SocialClientApi socialClientApi;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private MemberUserProducer memberUserProducer;

    @Resource
    private OrderService orderService;

    @Resource
    private OrderDetailService orderDetailService;
    @Resource
    private UserExpandService userExpandService;

    @Resource
    private AdminCardService adminCardService;

    @Resource
    private MemberGroupMapper memberGroupMapper;

    @Resource
    private MemberTagService memberTagService;


    @Override
    public MemberUserDO getUserByMobile(String mobile) {
        return memberUserMapper.selectByMobile(mobile);
    }

    @Override
    public List<MemberUserDO> getUserListByNickname(String nickname) {
        return memberUserMapper.selectListByNicknameLike(nickname);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public MemberUserDO createUserIfAbsent(String mobile, String registerIp, Integer terminal) {
        // 用户已经存在
        MemberUserDO user = memberUserMapper.selectByMobile(mobile);
        if (user != null) {
            return user;
        }
        // 用户不存在，则进行创建
        return createUser(mobile, null, null, registerIp, terminal);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public MemberUserDO createUser(String nickname, String avtar, String registerIp, Integer terminal) {
        return createUser(null, nickname, avtar, registerIp, terminal);
    }

    private MemberUserDO createUser(String mobile, String nickname, String avtar,
                                    String registerIp, Integer terminal) {
        // 生成密码
        String password = IdUtil.fastSimpleUUID();
        // 插入用户
        MemberUserDO user = new MemberUserDO();
        user.setMobile(mobile);
        user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
        user.setPassword(encodePassword(password)); // 加密密码
        user.setRegisterIp(registerIp).setRegisterTerminal(terminal);
        user.setNickname(nickname).setAvatar(avtar); // 基础信息
        if (StrUtil.isEmpty(nickname)) {
            // 昵称为空时，随机一个名字，避免一些依赖 nickname 的逻辑报错，或者有点丑。例如说，短信发送有昵称时~
            user.setNickname("用户" + RandomUtil.randomNumbers(6));
        }
        memberUserMapper.insert(user);

        // 发送 MQ 消息：用户创建
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {

            @Override
            public void afterCommit() {
                memberUserProducer.sendUserCreateMessage(user.getId());
            }

        });
        return user;
    }

    @Override
    public void updateUserLogin(Long id, String loginIp) {
        memberUserMapper.updateById(new MemberUserDO().setId(id)
                .setLoginIp(loginIp).setLoginDate(LocalDateTime.now()));
    }

    @Override
    public MemberUserDO getUser(Long id) {
        return memberUserMapper.selectById(id);
    }

    @Override
    public List<MemberUserDO> getUserList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return ListUtil.empty();
        }
        return memberUserMapper.selectBatchIds(ids);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUser(Long userId, AppMemberUserUpdateReqVO reqVO) {
        // 校验存在
        validateUserExists(userId);
        // 校验手机唯一
        validateMobileUnique(userId, reqVO.getMobile());

        MemberUserDO updateObj = BeanUtils.toBean(reqVO, MemberUserDO.class).setId(userId);
        memberUserMapper.updateById(updateObj);
        userExpandService.updateUserExpandByuserId(userId,reqVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUserMobile(Long userId, AppMemberUserUpdateMobileReqVO reqVO) {
        // 1.1 检测用户是否存在
        MemberUserDO user = validateUserExists(userId);
        // 1.2 校验新手机是否已经被绑定
        validateMobileUnique(null, reqVO.getMobile());

        // 2.1 校验旧手机和旧验证码
        // 补充说明：从安全性来说，老手机也校验 oldCode 验证码会更安全。但是由于 uni-app 商城界面暂时没做，所以这里不强制校验
        if (StrUtil.isNotEmpty(reqVO.getOldCode())) {
            smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(user.getMobile()).setCode(reqVO.getOldCode())
                    .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP()));
        }
        // 2.2 使用新验证码
        smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(reqVO.getMobile()).setCode(reqVO.getCode())
                .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP()));

        // 3. 更新用户手机
        memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(reqVO.getMobile()).build());
    }

    @Override
    public void updateUserMobileByWeixin(Long userId, AppMemberUserUpdateMobileByWeixinReqVO reqVO) {
        // 1.1 获得对应的手机号信息
        SocialWxPhoneNumberInfoRespDTO phoneNumberInfo = socialClientApi.getWxMaPhoneNumberInfo(
                UserTypeEnum.MEMBER.getValue(), reqVO.getCode());
        Assert.notNull(phoneNumberInfo, "获得手机信息失败，结果为空");
        // 1.2 校验新手机是否已经被绑定
        validateMobileUnique(userId, phoneNumberInfo.getPhoneNumber());

        // 2. 更新用户手机
        memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(phoneNumberInfo.getPhoneNumber()).build());
    }

    @Override
    public void updateUserPassword(Long userId, AppMemberUserUpdatePasswordReqVO reqVO) {
        // 检测用户是否存在
        MemberUserDO user = validateUserExists(userId);
        // 校验验证码
        smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(user.getMobile()).setCode(reqVO.getCode())
                .setScene(SmsSceneEnum.MEMBER_UPDATE_PASSWORD.getScene()).setUsedIp(getClientIP()));

        // 更新用户密码
        memberUserMapper.updateById(MemberUserDO.builder().id(userId)
                .password(passwordEncoder.encode(reqVO.getPassword())).build());
    }

    @Override
    public void resetUserPassword(AppMemberUserResetPasswordReqVO reqVO) {
        // 检验用户是否存在
        MemberUserDO user = validateUserExists(reqVO.getMobile());

        // 使用验证码
        smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_RESET_PASSWORD,
                getClientIP()));

        // 更新密码
        memberUserMapper.updateById(MemberUserDO.builder().id(user.getId())
                .password(passwordEncoder.encode(reqVO.getPassword())).build());
    }

    private MemberUserDO validateUserExists(String mobile) {
        MemberUserDO user = memberUserMapper.selectByMobile(mobile);
        if (user == null) {
            throw exception(USER_MOBILE_NOT_EXISTS);
        }
        return user;
    }

    @Override
    public boolean isPasswordMatch(String rawPassword, String encodedPassword) {
        return passwordEncoder.matches(rawPassword, encodedPassword);
    }

    /**
     * 对密码进行加密
     *
     * @param password 密码
     * @return 加密后的密码
     */
    private String encodePassword(String password) {
        return passwordEncoder.encode(password);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUser(MemberUserUpdateReqVO updateReqVO) {
        // 校验存在
        MemberUserDO memberUserDO = validateUserExists(updateReqVO.getId());
        // 校验手机唯一
        validateMobileUnique(updateReqVO.getId(), updateReqVO.getMobile());
        // 校验卡号
        if(StringUtils.isNotEmpty(updateReqVO.getCardId())){
            adminCardService.checkCard(updateReqVO.getCardId());
        }
        List<MemberUserDO> memberUserDOS = memberUserMapper.selectList(Wrappers.<MemberUserDO>lambdaQuery()
                .eq(MemberUserDO::getCardId, updateReqVO.getCardId())
                .ne(MemberUserDO::getId,updateReqVO.getId()));
        if(memberUserDOS.size()>0){
            throw exception(CARD_ALREADY_BIND);
        }
        //删除人脸
        if(memberUserDO.getFaceId()!=null){
            memberUserMapper.deleteFace(memberUserDO.getFaceId());
        }

        // 更新
        MemberUserDO updateObj = MemberUserConvert.INSTANCE.convert(updateReqVO);
        updateObj.setCardId(StringUtils.isBlank(updateReqVO.getCardId())?null:updateReqVO.getCardId());
        memberUserMapper.updateById(updateObj);
    }

    @VisibleForTesting
    MemberUserDO validateUserExists(Long id) {
        if (id == null) {
            return null;
        }
        MemberUserDO user = memberUserMapper.selectById(id);
        if (user == null) {
            throw exception(USER_NOT_EXISTS);
        }
        return user;
    }

    @VisibleForTesting
    void validateMobileUnique(Long id, String mobile) {
        if (StrUtil.isBlank(mobile)) {
            return;
        }
        MemberUserDO user = memberUserMapper.selectByMobile(mobile);
        if (user == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的用户
        if (id == null) {
            throw exception(USER_MOBILE_USED, mobile);
        }
        if (!user.getId().equals(id)) {
            throw exception(USER_MOBILE_USED, mobile);
        }
    }

    @Override
    public PageResult<MemberUserDO> getUserPage(MemberUserPageReqVO pageReqVO) {
        if(pageReqVO.getGroupId()!=null){
            pageReqVO.setUserIds(memberGroupMapper.getMemberList(pageReqVO.getGroupId()));
        }
        return memberUserMapper.selectPage(pageReqVO);
    }

    @Override
    public void updateUserLevel(Long id, Long levelId, Integer experience) {
        // 0 代表无等级：防止UpdateById时，会被过滤掉的问题
        levelId = ObjectUtil.defaultIfNull(levelId, 0L);
        memberUserMapper.updateById(new MemberUserDO()
                .setId(id)
                .setLevelId(levelId).setExperience(experience)
        );
    }

    @Override
    public Long getUserCountByGroupId(Long groupId) {
        return memberUserMapper.selectCountByGroupId(groupId);
    }

    @Override
    public Long getUserCountByLevelId(Long levelId) {
        return memberUserMapper.selectCountByLevelId(levelId);
    }

    @Override
    public Long getUserCountByTagId(Long tagId) {
        return memberUserMapper.selectCountByTagId(tagId);
    }

    @Override
    public boolean updateUserPoint(Long id, Integer point) {
        if (point > 0) {
            memberUserMapper.updatePointIncr(id, point);
        } else if (point < 0) {
            return memberUserMapper.updatePointDecr(id, point) > 0;
        }
        return true;
    }

    @Override
    public Boolean bindCard(String cardId) {
        adminCardService.checkCard(cardId);
        List<MemberUserDO> memberUserDOS = memberUserMapper.selectList(Wrappers.<MemberUserDO>lambdaQuery().eq(MemberUserDO::getCardId, cardId));
        if(memberUserDOS.size()>0){
            throw exception(CARD_ALREADY_BIND);
        }
        MemberUserDO memberUserDO = memberUserMapper.selectById(SecurityFrameworkUtils.getLoginUserId());
        memberUserDO.setCardId(cardId);
        return memberUserMapper.updateById(memberUserDO) > 0;
    }

    @Override
    public Boolean checkCard(Long userId) {
        MemberUserDO memberUserDO = memberUserMapper.selectById(userId);
        return StrUtil.isNotBlank(memberUserDO.getCardId());
    }

    @Override
    public List<AppNutritionDayVo> nutritionDay(String start, String end) {
        List<AppNutritionDayVo> result = new ArrayList<>();

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(MemberConstants.TIME_FORMAT);

        LocalDateTime startTime = LocalDateTime.parse(start, formatter);
        LocalDateTime endTime = LocalDateTime.parse(end, formatter);
        List<DishOrderDO> dishOrderDOS = orderService.getDishOrderByTime(startTime, endTime);
        List<Long> moIds = new ArrayList<>();
        List<Long> miIds = new ArrayList<>();
        List<Long> niIds = new ArrayList<>();
        for (DishOrderDO orderDO : dishOrderDOS){
            int hour = orderDO.getCreateTime().getHour();
            //早
            if(TimePeriodEnum.MORNING.getStartInt()<=hour && hour<=TimePeriodEnum.MORNING.getEndInt()){
                moIds.add(orderDO.getId());
            } else if (TimePeriodEnum.MIDDAY.getStartInt()<=hour && hour<=TimePeriodEnum.MIDDAY.getEndInt()){
                miIds.add(orderDO.getId());
            }else{
                niIds.add(orderDO.getId());
            }
        }
        if(CollectionUtil.isNotEmpty(moIds)){
            List<OrderDetailDO> orderDetailDOS = orderDetailService.selectListByOrderIds(moIds);
            AppNutritionDayVo appNutritionDayVo = new AppNutritionDayVo();
            BigDecimal reduce = orderDetailDOS.stream()
                    .filter(f-> ObjectUtil.isNotEmpty(f.getHeat()))
                    .map(m->BigDecimal.valueOf(m.getHeat()))
                    .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.HALF_UP);
            appNutritionDayVo.setIntake(reduce);
            appNutritionDayVo.setProposeIntake(MemberConstants.PROPOSE_MORNING);
            appNutritionDayVo.setTimePeriod("1");
            appNutritionDayVo.setDetailList(orderDetailDOS);
            result.add(appNutritionDayVo);
        }
        if(CollectionUtil.isNotEmpty(miIds)){
            List<OrderDetailDO> orderDetailDOS = orderDetailService.selectListByOrderIds(miIds);
            AppNutritionDayVo appNutritionDayVo = new AppNutritionDayVo();
            BigDecimal reduce = orderDetailDOS.stream()
                    .filter(f-> ObjectUtil.isNotEmpty(f.getHeat()))
                    .map(m->BigDecimal.valueOf(m.getHeat()))
                    .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.HALF_UP);
            appNutritionDayVo.setIntake(reduce);
            appNutritionDayVo.setProposeIntake(MemberConstants.PROPOSE_NOON);
            appNutritionDayVo.setTimePeriod("2");
            appNutritionDayVo.setDetailList(orderDetailDOS);
            result.add(appNutritionDayVo);
        }
        if(CollectionUtil.isNotEmpty(niIds)){
            List<OrderDetailDO> orderDetailDOS = orderDetailService.selectListByOrderIds(niIds);
            AppNutritionDayVo appNutritionDayVo = new AppNutritionDayVo();
            BigDecimal reduce = orderDetailDOS.stream()
                    .filter(f-> ObjectUtil.isNotEmpty(f.getHeat()))
                    .map(m->BigDecimal.valueOf(m.getHeat()))
                    .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.HALF_UP);
            appNutritionDayVo.setIntake(reduce);
            appNutritionDayVo.setProposeIntake(MemberConstants.PROPOSE_NIGHT);
            appNutritionDayVo.setTimePeriod("3");
            appNutritionDayVo.setDetailList(orderDetailDOS);
            result.add(appNutritionDayVo);
        }
        return result;
    }

    @Override
    public AppWeekVO NutritionWeek(String start, String end) {
        AppWeekVO appWeekVO = new AppWeekVO();
        List<AppNutritionWeekVO> result = new ArrayList<>();
        //获取日期的订单
        //String newStart = start + " 00:00:00";
        //String newEnd = end + " 23:59:59";
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(MemberConstants.TIME_FORMAT);
        DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern(MemberConstants.DATE_FORMAT);
        LocalDateTime startTime = LocalDateTime.parse(start, formatter);
        LocalDateTime endTime = LocalDateTime.parse(end, formatter);
        List<DishOrderDO> dishOrderDOS = orderService.getDishOrderByTime(startTime, endTime);

        WeekFields weekFields = WeekFields.ISO;
        for (Long i = 1L; i <= 7; i++) {
            LocalDateTime mondayDate = startTime.with(weekFields.dayOfWeek(), i);
            String format = mondayDate.format(formatter1);
            List<Long> ids = dishOrderDOS.stream().filter(orderDO -> format.equals(orderDO.getCreateTime().format(formatter1)))
                    .map(DishOrderDO::getId).collect(Collectors.toList());
            AppNutritionWeekVO mondayVO = new AppNutritionWeekVO();
            mondayVO.setTime(format);
            if (CollectionUtil.isNotEmpty(ids)) {
                List<OrderDetailDO> orderDetailDOS = orderDetailService.selectListByOrderIds(ids);
                mondayVO.setDetailList(orderDetailDOS);
                mondayVO.setIntake(orderDetailDOS.stream().filter(vo->vo!=null && vo.getHeat()!=null).map(OrderDetailDO::getHeat).reduce(0d, Double::sum));
            }
            result.add(mondayVO);
        }
        appWeekVO.setNum(dishOrderDOS.size());
        appWeekVO.setList(result);
        return appWeekVO;
    }

    @Override
    public List<DishesNutritionRespDTO> getHeatDishes(Long dishesId) {
        List<DishesNutritionRespDTO> dishesList = dishesNutritionApil.getDishesList(dishesId);
        this.computePercentage(dishesList);
        return dishesList;
    }

    @Override
    public List<Map> getUserHeat(Long userId, LocalDateTime startDate,LocalDateTime endDate,Long orderId) {
        if(ObjectUtil.isNotEmpty(orderId)){
            return   suggestion(userId,null,null,Boolean.FALSE,orderId);
        }else{
            return   suggestion(userId,startDate,endDate,Boolean.TRUE,null);
        }
    }
    public  List<Map>  suggestion(Long userId,LocalDateTime startDate,LocalDateTime endDate,Boolean bol,Long orderId) {
        //查询今天的订单
        List<Long> collect= new ArrayList<>();
        if(ObjectUtil.isNotEmpty(orderId)){
            collect= Collections.singletonList(orderId);
        }
        if(ObjectUtil.isNotEmpty(userId)){
            collect = dishOrderMapper.selectList(new LambdaQueryWrapperX<DishOrderDO>()
                            .eqIfPresent(DishOrderDO::getUserId, userId)
                            .eq(DishOrderDO::getOrderStatus, DishOrderDO.COMPLETE)
                            .betweenIfPresent(DishOrderDO::getCreateTime, startDate, endDate))
                    .stream()
                    .map(DishOrderDO::getId)
                    .collect(Collectors.toList());
        }
        if(ObjectUtil.isEmpty(collect)){
            return Collections.emptyList();
        }
        //获取今天购买的菜品id
        List<OrderDetailDO> orderDetailDOS = orderDetailMapper.selectList(new LambdaQueryWrapperX<OrderDetailDO>()
                .inIfPresent(OrderDetailDO::getOrderId, collect));
        Map<Long, List<OrderDetailDO>> collect2 = orderDetailDOS.stream().collect(Collectors.groupingBy(OrderDetailDO::getDishesId));
        //热量求和统计方便  科学膳食小建议
        double sum = orderDetailDOS
                .stream().filter(vo->vo!=null && vo.getHeat()!=null)
                .mapToDouble(OrderDetailDO::getHeat).sum();
        List<DishesNutritionRespDTO> list=new ArrayList<>();
        for (Map.Entry<Long, List<OrderDetailDO>> entry1 : collect2.entrySet()) {
            List<OrderDetailDO> value1 = entry1.getValue();
            //获取菜品id
            List<Long> collect1 = value1.stream()
                    .map(OrderDetailDO::getDishesId)
                    .collect(Collectors.toList());
            //获取菜品营养信息
            List<DishesNutritionRespDTO> dishesList=new ArrayList<>();
            collect1.forEach(x-> {
                List<DishesNutritionRespDTO> dishesList1 = dishesNutritionApil.getDishesList(x);
                if ( CollectionUtil.isNotEmpty(dishesList1)){
                    dishesList.addAll(dishesList1);
                }
            });
            //根据营养名称进行分组
            if(ObjUtil.isNotEmpty(dishesList)){
                BigDecimal reduce = value1.stream()
                        .map(OrderDetailDO::getWeight)
                        .filter(ObjectUtil::isNotEmpty)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                Map<String, List<DishesNutritionRespDTO>> map = dishesList.stream()
                        .collect(Collectors.groupingBy(DishesNutritionRespDTO::getNutritionName));
                for (Map.Entry<String, List<DishesNutritionRespDTO>> entry : map.entrySet()) {
                    DishesNutritionRespDTO dto = getDishesNutritionRespDTO(entry, reduce);
                    list.add(dto);
                }
            }
        }
        list = this.groupingBy(list);
        this.computePercentage(list);
        //转map
        List<Map> maps = BeanUtil.copyToList(list, Map.class);
        //判断是否需要 科学膳食小建议
        if(Boolean.TRUE.equals(bol)){
            Map<String, String> build = MapUtil.builder(new HashMap<String, String>()).build();
            if(sum<=MemberConstants.STANDARDS){
                build.put("prompt",MemberConstants.INSUFFICIENT_PROPOSAL);
            }else if(sum<MemberConstants.EXCEED_STANDARDS){
                build.put("prompt",MemberConstants.STANDARDS_PROPOSAL);
            }else{
                build.put("prompt",MemberConstants.EXCEED_PROPOSAL);
            }
            maps.add(build);
        }
        return maps;
    }

    private static DishesNutritionRespDTO getDishesNutritionRespDTO(Map.Entry<String, List<DishesNutritionRespDTO>> entry, BigDecimal reduce) {
        DishesNutritionRespDTO dto = new DishesNutritionRespDTO();
        //计算当前菜品
        List<DishesNutritionRespDTO> value = entry.getValue();
        //当前营养的总和
        BigDecimal sum1 = new BigDecimal(value.get(0).getNutritionNumber());
        //计算营养乘数  菜品总量/100g=营养乘数
        BigDecimal divide = reduce.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP);
        BigDecimal bigDecimal = sum1.multiply(divide).setScale(0, RoundingMode.HALF_UP);
        dto.setNutritionName(entry.getKey());
        dto.setNutritionNumber(bigDecimal.toString());
        return dto;
    }

    /**
     * @Description: 根据营养名称进行分组计算值
     * @Author: qjq
     * @Date: 2024/4/12 下午2:44 
     * @return 
     */
    public List<DishesNutritionRespDTO> groupingBy(List<DishesNutritionRespDTO> list){
        List<DishesNutritionRespDTO> list1=new ArrayList<>();
        Map<String, List<DishesNutritionRespDTO>> collect = list.stream()
                .collect(Collectors.groupingBy(DishesNutritionRespDTO::getNutritionName));
        for (Map.Entry<String, List<DishesNutritionRespDTO>> entry : collect.entrySet()) {
            DishesNutritionRespDTO dto=new DishesNutritionRespDTO();
            BigDecimal reduce = entry.getValue()
                    .stream()
                    .filter(f -> ObjectUtil.isNotEmpty(f.getNutritionNumber()))
                    .map(m -> new BigDecimal(m.getNutritionNumber()))
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            String key = entry.getKey();
            dto.setNutritionName(key);
            dto.setNutritionNumber(reduce.setScale(0, RoundingMode.HALF_UP).toString());
            list1.add(dto);
        }
        return list1;
    }
    public void computePercentage(List<DishesNutritionRespDTO> list){
        //获取所有营养数量
        BigDecimal sum = list.stream()
                .filter(f-> ObjectUtil.isNotEmpty(f.getNutritionNumber()))
                .map(m-> new BigDecimal(m.getNutritionNumber()))
                .reduce(BigDecimal.ZERO,BigDecimal::add);
        BigDecimal bigDecimal = new BigDecimal(0);
        for (int i = 0; i < list.size(); i++) {
            if(i < (list.size()-1)){
                DishesNutritionRespDTO dto = list.get(i);
                BigDecimal bigDecimal1 = new BigDecimal(dto.getNutritionNumber());
                BigDecimal div = NumberUtil.div(bigDecimal1, sum, 4);
                BigDecimal mul = NumberUtil.mul(div, 100);
                bigDecimal=bigDecimal.add(mul);
                list.get(i).setNutritionPer(mul.floatValue()+"%");
            }else{
                list.get(i).setNutritionPer(NumberUtil.sub(100,bigDecimal).doubleValue()+"%");
            }
        }
    }

    @Override
    public Boolean bindFace(Long userId,Long faceId) {

        MemberUserDO memberUserDO = memberUserMapper.selectById(userId);
        if(memberUserDO == null){
            throw exception(USER_NOT_EXISTS);
        }
//        if(memberUserDO.getFaceId()!=null){
//            memberUserMapper.deleteFace(memberUserDO.getFaceId());
//        }
        memberUserDO.setFaceId(faceId);
        return memberUserMapper.updateById(memberUserDO)>0;
    }


    /**
     * @param platesNum
     * @return
     * @Description:
     * @Author: qjq
     * @Date: 2024/4/10 下午2:30
     */
    @Override
    public Map<String,String> getDishesList(String platesNum) {
        DiningPlatesDO diningPlatesDO = diningPlatesMapper.selectOne(new LambdaQueryWrapperX<DiningPlatesDO>()
                .eq(DiningPlatesDO::getDiningPlatesNum,platesNum));
        //获取用户id
        Long userId = diningPlatesDO.getUserId();
        //获取用户名称
        MemberUserDO memberUserDO = memberUserMapper.selectById(userId);
        BigDecimal moneyByUserId = cardService.getMoneyByUserId(userId);
        Map<String, String> map = new HashMap<>();
        map.put("name",memberUserDO.getName());
        map.put("money", String.valueOf(moneyByUserId));
        return map;
    }

    @Override
    public MemberUserDO create(MemberUserAddVO addVO) {
        if(StringUtils.isNotEmpty(addVO.getCardId())){
            adminCardService.checkCard(addVO.getCardId());
        }
        // 用户已经存在
        MemberUserDO userSelect = memberUserMapper.selectByMobile(addVO.getMobile());
        if (userSelect != null) {
            return userSelect;
        }
        // 用户不存在，则进行创建
        // 生成密码
        String password = IdUtil.fastSimpleUUID();
        // 插入用户
        MemberUserDO user = new MemberUserDO();
        BeanUtil.copyProperties(addVO,user);
        user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
        user.setPassword(encodePassword(password)); // 加密密码
        memberUserMapper.insert(user);
        return user;
    }

    @Override
    public PageResult<MemberUserDO> getGroupUserList(MemberUserListVO listVO) {
        return memberUserMapper.selectUserList(listVO);
    }

    @Override
    public String getCardId(Long userId) {
        if(userId == null){
             userId = getLoginUserId();
        }
        MemberUserDO memberUserDO = memberUserMapper.selectById(userId);
        return memberUserDO.getCardId();
    }

    @Override
    public BigDecimal getReductionAmount(Long userId, BigDecimal money,LocalDateTime time) {

        String timePeriod = TimePeriodEnum.getTimePeriod(time);

        MemberUserDO memberUserDO = memberUserMapper.selectById(userId);
        List<Long> tagIds = memberUserDO.getTagIds();
        if (CollectionUtil.isEmpty(tagIds)){
            return BigDecimal.ZERO;
        }
        List<MemberTagDO> tagList = memberTagService.getTagList(tagIds);

        if(tagList.size()>1){
            //排序
            List<MemberTagDO> collect = tagList.stream().sorted(Comparator.comparing(MemberTagDO::getState))
                    .collect(Collectors.toList());
            return countAmount(collect.get(0),money,timePeriod);
        }else {
            return countAmount(tagList.get(0),money,timePeriod);
        }
    }

    @Override
    public String getFaceUrl(Long userId) {
        return memberUserMapper.getFace(userId);
    }

    @Override
    public List<MemberUserDO> getStoreUser() {
        return memberUserMapper.selectList();
    }

    public BigDecimal countAmount(MemberTagDO memberTagDO, BigDecimal money, String timePeriod ){
        if(memberTagDO.getType().equals("1")){
            if (memberTagDO.getTimeFlag().equals("0")){
                return money.multiply(memberTagDO.getProportion()).setScale(2, RoundingMode.HALF_UP);
            }else {
                BigDecimal bigDecimal;
                switch (timePeriod){
                    case "2":
                        bigDecimal = money.multiply(memberTagDO.getMorning()).setScale(2, RoundingMode.HALF_UP);
                        break;
                    case "3":
                        bigDecimal = money.multiply(memberTagDO.getNoon()).setScale(2, RoundingMode.HALF_UP);
                        break;
                    case "4":
                        bigDecimal = money.multiply(memberTagDO.getNight()).setScale(2, RoundingMode.HALF_UP);
                        break;
                    default:
                        bigDecimal = BigDecimal.ZERO;
                        break;
                }
                return bigDecimal;
            }

        }
        if(memberTagDO.getType().equals("2")){
            if (memberTagDO.getTimeFlag().equals("0")){
                return memberTagDO.getAmount();
            }else {
                BigDecimal bigDecimal;
                switch (timePeriod){
                    case "2":
                        bigDecimal = memberTagDO.getMorning();
                        break;
                    case "3":
                        bigDecimal = memberTagDO.getNoon();
                        break;
                    case "4":
                        bigDecimal = memberTagDO.getNight();
                        break;
                    default:
                        bigDecimal = BigDecimal.ZERO;
                        break;
                }
                return bigDecimal;
            }

        }
        return BigDecimal.ZERO;
    }
}
