package com.bonus.core.menu.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bonus.common.core.exception.ServiceException;
import com.bonus.constant.DelFlagEnum;
import com.bonus.core.allocation.api.AllocDeliveryApi;
import com.bonus.core.allocation.api.AllocMealtimeApi;
import com.bonus.core.allocation.canteen.model.AllocDeliveryCostModel;
import com.bonus.core.marketing.constants.MktEffTypeEnum;
import com.bonus.core.marketing.constants.MktUserTypeEnum;
import com.bonus.core.marketing.vo.MktEffectiveUserVO;
import com.bonus.core.menu.dto.AppletReserveCanteenDTO;
import com.bonus.core.menu.dto.AppletReserveRecipeDTO;
import com.bonus.core.menu.dto.AppletWeekCanteenDTO;
import com.bonus.core.menu.dto.AppletWeekRecipeDTO;
import com.bonus.core.menu.entity.MenuRecipe;
import com.bonus.core.menu.entity.MenuRecipeDetail;
import com.bonus.core.menu.entity.MenuRecipeDishes;
import com.bonus.core.menu.mapper.MenuDishesMapper;
import com.bonus.core.menu.mapper.MenuDishesSaleRecordMapper;
import com.bonus.core.menu.mapper.MenuRecipeMapper;
import com.bonus.core.menu.model.AllocMealtimeStateModel;
import com.bonus.core.menu.model.MenuStallSaleModel;
import com.bonus.core.menu.service.MenuRecipeDetailService;
import com.bonus.core.menu.service.MenuRecipeDishesService;
import com.bonus.core.menu.service.MenuRecipeService;
import com.bonus.core.menu.mapper.MenuAppRecipeMapper;
import com.bonus.core.menu.vo.*;
import com.bonus.core.nutrition.api.NutritionApi;
import com.bonus.core.nutrition.common.po.DishNutritionParam;
import com.bonus.core.nutrition.common.vo.HealthDishRecommendScoreVO;
import com.bonus.domain.CustInfo;
import com.bonus.utils.id.Id;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class MenuRecipeServiceImpl extends ServiceImpl<MenuRecipeMapper, MenuRecipe> implements MenuRecipeService {
    private static final Logger log = LoggerFactory.getLogger(MenuRecipeServiceImpl.class);

    @Autowired
    private MenuRecipeDetailService menuRecipeDetailService;

    @Autowired
    private MenuRecipeDishesService menuRecipeDishesService;

    @Autowired
    private MenuAppRecipeMapper menuAppRecipeMapper;

    @Autowired
    private MenuDishesSaleRecordMapper menuDishesSaleRecordMapper;

    @Autowired
    private MenuRecipeMapper menuRecipeMapper;

    @Autowired
    private AllocDeliveryApi allocDeliveryApi;

    @Autowired
    private AllocMealtimeApi allocMealtimeApi;

    @Autowired
    private MenuDishesMapper menuDishesMapper;

    @Autowired
    private NutritionApi nutritionApi;

    @Override
    public List<AppletWeekCanteenVO> getWeekMealList(AppletWeekCanteenDTO content) {
        Set<Long> effIdSet = this.getShopstallIdListByCustId(content.getCustId(), false);
        if (ObjectUtil.isEmpty(effIdSet)) {
            effIdSet = Sets.newTreeSet();
            ((Set) effIdSet).add(-1L);
        }

        return this.menuAppRecipeMapper.selectWeekCanteenList((Set) effIdSet);
    }

    public Set<Long> getShopstallIdListByCustId(Long custId, boolean checkCustInfo) {
        Set<Long> effIdSet = Sets.newTreeSet();
        CustInfo custInfoQuery = new CustInfo();
        custInfoQuery.setCustId(custId);
        custInfoQuery.setCustState(1);
        CustInfo custInfo = menuRecipeMapper.selectOrgAndPsnByCustLimitId(custInfoQuery);
        if (ObjectUtil.isNull(custInfo)) {
            if (checkCustInfo) {
                throw new ServiceException("菜单绑定错误");
            } else {
                log.info("***[获取全档口菜谱信息]_未查询到人员信息,直接返回********************");
                return (Set) effIdSet;
            }
        } else {
            List<MktEffectiveUserVO> effectiveUserVOList = menuRecipeMapper.selectAllUserEff(MktEffTypeEnum.USER_SORT.key(), DelFlagEnum.DEL_FALSE.key());
            Long orgId = custInfo.getOrgId();
            Integer psnType = custInfo.getPsnType();
            if (ObjectUtil.isNotEmpty(effectiveUserVOList)) {
                List<MktEffectiveUserVO> collect = (List) effectiveUserVOList.stream().filter((u) -> {
                    boolean containsOrg = ObjectUtil.isNotEmpty(u.getOrgIdList()) && u.getOrgIdList().contains(orgId);
                    boolean containsPsn = ObjectUtil.isNotEmpty(u.getPsnTypeList()) && u.getPsnTypeList().contains(psnType);
                    return MktUserTypeEnum.INTERSECTION.key().equals(u.getUserType()) && containsOrg && containsPsn || MktUserTypeEnum.UNION_SET.key().equals(u.getUserType()) && (containsOrg || containsPsn);
                }).collect(Collectors.toList());
                if (ObjectUtil.isNotEmpty(collect)) {
                    effIdSet = (Set) collect.stream().map(MktEffectiveUserVO::getEffId).collect(Collectors.toSet());
                    ((Set) effIdSet).add(-1L);
                }
            }

            log.info("***[获取指定人员折扣比例]_指定人员的生效范围effIdSet: {}", effIdSet);
            return (Set) effIdSet;
        }
    }

    @Override
    public List<AppletWeekRecipeVO> getWeekRecipeDetailList(AppletWeekRecipeDTO content) {
        List<Long> recipeIdList = Lists.newArrayList();
        if (ObjectUtil.isNull(content.getRecipeId())) {
            Set<Long> effIdSet = this.getShopstallIdListByCustId(content.getCustId(), false);
            if (ObjectUtil.isEmpty(effIdSet)) {
                effIdSet = Sets.newTreeSet();
                ((Set) effIdSet).add(-1L);
            }

            recipeIdList = this.menuAppRecipeMapper.selectWeekRecipeId(content.getRecipeId(), (Set) effIdSet);
            if (ObjectUtil.isEmpty(recipeIdList)) {
                return Lists.newArrayList();
            }
        } else {
            ((List) recipeIdList).add(content.getRecipeId());
        }

        this.generateRecipe((List) recipeIdList, content.getApplyDate());
        List<AppletWeekRecipeVO> resultList = menuRecipeMapper.selectWeekRecipe(content.getApplyDate(), content.getRecipeId(), (List) recipeIdList);
        resultList.sort(Collections.reverseOrder((s1, s2) -> {
            return s2.getMealtimeType() - s1.getMealtimeType();
        }));
        return resultList;
    }

    @Override
    public List<AppletReserveCanteenVO> getReserveMealCanteenList(AppletReserveCanteenDTO content) {
        Set<Long> effIdSet = this.getShopstallIdListByCustId(content.getCustId(), false);
        if (ObjectUtil.isEmpty(effIdSet)) {
            effIdSet = Sets.newTreeSet();
            ((Set)effIdSet).add(-1L);
        }
        List<AppletReserveCanteenVO> resultList = this.menuAppRecipeMapper.selectReserveMealCanteenList((Set)effIdSet);
        if (ObjectUtil.isEmpty(resultList)) {
            return resultList;
        } else {
            List<Long> stallIdList = (List)resultList.stream().flatMap((canteen) -> {
                return canteen.getStallList().stream().map(AppletReserveStallVO::getStallId);
            }).collect(Collectors.toList());
            if (ObjectUtil.isNotEmpty(stallIdList)) {
                List<MenuStallSaleModel> stallMonthSalesList = this.menuDishesSaleRecordMapper.selectMonthSalesStall(stallIdList);
                Map<Long, Integer> stallSaleMap = (Map)stallMonthSalesList.stream().collect(Collectors.toMap(MenuStallSaleModel::getStallId, MenuStallSaleModel::getMonthlySales));
                resultList.forEach((canteen) -> {
                    List<AppletReserveStallVO> stallList = canteen.getStallList();
                    if (ObjectUtil.isNotEmpty(stallList)) {
                        stallList.forEach((stall) -> {
                            stall.setMonthlySales((Integer)stallSaleMap.get(stall.getStallId()));
                        });
                    }

                });
            }

            resultList.forEach((c) -> {
                List<AppletReserveStallVO> stallList = c.getStallList();
                if (ObjectUtil.isNotEmpty(stallList)) {
                    stallList.forEach((s) -> {
                        List<Long> topDishesIdList = this.menuDishesSaleRecordMapper.selectTopDishesIdList(Collections.singletonList(s.getStallId()));
                        List<AppletRecommendDishesVO> recommendList = Lists.newArrayList();
                        List<AppletRecommendDishesVO> allRecipeDishesList = ((MenuRecipeMapper)this.baseMapper).selectAllRecipeDishesList(s.getRecipeId(), (Integer)null);
                        if (ObjectUtil.isNotEmpty(allRecipeDishesList) && ObjectUtil.isNotEmpty(topDishesIdList)) {
                            Iterator var5 = topDishesIdList.iterator();

                            while(var5.hasNext()) {
                                Long dishesId = (Long)var5.next();
                                if (allRecipeDishesList.stream().map(AppletRecommendDishesVO::getDishesId).collect(Collectors.toList()).contains(dishesId)) {
                                    AppletRecommendDishesVO recommendDishes = (AppletRecommendDishesVO)allRecipeDishesList.stream().filter((a) -> {
                                        return a.getDishesId().equals(dishesId);
                                    }).collect(Collectors.toList()).get(0);
                                    if (!recommendList.contains(recommendDishes)) {
                                        recommendList.add(recommendDishes);
                                    }

                                    if (recommendList.size() >= 3) {
                                        s.setRecommendList(recommendList);
                                        return;
                                    }
                                }
                            }
                        }

                        s.setRecommendList(recommendList);
                    });
                }

            });
            return resultList;
        }
    }

    @Override
    public List<AppletReserveRecipeVO> getReserveRecipeDetailList(AppletReserveRecipeDTO content) {
        this.generateRecipe(content.getRecipeId(), content.getApplyDate());
        List<AppletReserveRecipeVO> resultList = ((MenuRecipeMapper)this.baseMapper).selectReserveRecipe(content.getApplyDate(), content.getRecipeId());
        if (ObjectUtil.isEmpty(resultList)) {
            return resultList;
        } else {
            resultList.sort(Collections.reverseOrder((s1, s2) -> {
                return s2.getMealtimeType() - s1.getMealtimeType();
            }));
            Long stallId = ((MenuRecipeMapper)this.baseMapper).selectStallIdByWrappers(Wrappers.lambdaQuery(MenuRecipe.class).eq(MenuRecipe::getRecipeId, content.getRecipeId()).eq(MenuRecipe::getDelFlag, DelFlagEnum.DEL_FALSE.key()));
            List<AllocDeliveryCostModel> costModelList = this.allocDeliveryApi.queryDeliveryCostModel(stallId);
            log.info("配置api查询的档口配送,档口id: {}, 配置: {}", stallId, JSON.toJSONString(costModelList));
            List<AllocMealtimeStateModel> mealTimeList = this.allocMealtimeApi.getAllocMealtimeMenuModel(stallId).getMealtimeStateModelList();
            if (ObjectUtil.isNotEmpty(mealTimeList)) {
                mealTimeList = (List)mealTimeList.stream().filter((mealtimex) -> {
                    return mealtimex.getIfUse() == 1;
                }).collect(Collectors.toList());
            }

            log.info("配置api查询的档口餐次,档口id: {}, 配置: {}", stallId, JSON.toJSONString(mealTimeList));
            if (ObjectUtil.isEmpty(mealTimeList)) {
                log.info("未获取到开启餐次");
                return Lists.newArrayList();
            } else {
                if (LocalDate.now().isEqual(content.getApplyDate())) {
                    mealTimeList = (List)mealTimeList.stream().filter((time) -> {
                        return LocalTime.now().isBefore(time.getStartTime());
                    }).collect(Collectors.toList());
                }

                if (ObjectUtil.isEmpty(mealTimeList)) {
                    log.info("未获取到餐次");
                    return Lists.newArrayList();
                } else {
                    List<AllocMealtimeStateModel> finalMealTimeList = mealTimeList;
                    resultList = (List)resultList.stream().filter((time) -> {
                        return finalMealTimeList.stream().map(AllocMealtimeStateModel::getMealtimeType).collect(Collectors.toList()).contains(time.getMealtimeType());
                    }).collect(Collectors.toList());
                    resultList.forEach((time) -> {
                        List<AppletReserveRecipeTypeVO> typeList = time.getTypeList();
                        if (ObjectUtil.isNotEmpty(typeList)) {
                            typeList.forEach((type) -> {
                                List<AppletReserveRecipeDishesVO> dishesList = type.getDishesList();
                                if (ObjectUtil.isNotEmpty(dishesList)) {
                                    dishesList.forEach((dishes) -> {
                                        dishes.setCostModelList(costModelList);
                                    });
                                }

                            });
                        }

                    });
                    Set<Long> dishesIdSet = (Set)resultList.stream().flatMap((time) -> {
                        return time.getTypeList().stream().flatMap((type) -> {
                            return type.getDishesList().stream().map((item) -> {
                                return ((AppletCurrentDishesDetailVO)item.getDishesDetailList().get(0)).getDishesId();
                            });
                        });
                    }).collect(Collectors.toSet());
                    if (ObjectUtil.isEmpty(dishesIdSet)) {
                        return resultList;
                    } else {
                        List<DishNutritionParam> dishNutritionParamList = this.menuDishesMapper.selectNutrition(dishesIdSet);
                        HealthDishRecommendScoreVO dishesRecommendScore = this.nutritionApi.getDishesRecommendScore(content.getCustId(), dishNutritionParamList);
                        if (dishesRecommendScore.getRecommendFlag()) {
                            resultList.forEach((time) -> {
                                List<AppletReserveRecipeTypeVO> typeList = time.getTypeList();
                                if (ObjectUtil.isNotEmpty(typeList)) {
                                    typeList.forEach((type) -> {
                                        List<AppletReserveRecipeDishesVO> dishesList = type.getDishesList();
                                        if (ObjectUtil.isNotEmpty(dishesList)) {
                                            dishesList.forEach((dishes) -> {
                                                Integer initialScore = (Integer)dishesRecommendScore.getDishRecommendMap().get(((AppletCurrentDishesDetailVO)dishes.getDishesDetailList().get(0)).getDishesId());
                                                if (ObjectUtil.isNotNull(initialScore)) {
                                                    dishes.setInitialScore(initialScore);
                                                }

                                            });
                                        }

                                    });
                                }

                            });
                        }

                        Iterator var10 = resultList.iterator();

                        while(true) {
                            List typeList;
                            do {
                                if (!var10.hasNext()) {
                                    return resultList;
                                }

                                AppletReserveRecipeVO mealtime = (AppletReserveRecipeVO)var10.next();
                                typeList = mealtime.getTypeList();
                            } while(!ObjectUtil.isNotEmpty(typeList));

                            List<AppletReserveRecipeDishesVO> recommendList = Lists.newArrayList();
                            Iterator var14 = typeList.iterator();

                            while(var14.hasNext()) {
                                AppletReserveRecipeTypeVO type = (AppletReserveRecipeTypeVO)var14.next();
                                List<AppletReserveRecipeDishesVO> dishesList = type.getDishesList();
                                if (ObjectUtil.isNotEmpty(dishesList)) {
                                    List<AppletReserveRecipeDishesVO> tempList = (List)dishesList.stream().filter((d) -> {
                                        return d.getRecommendFlag() == 1;
                                    }).collect(Collectors.toList());
                                    if (ObjectUtil.isNotEmpty(tempList)) {
                                        recommendList.addAll(tempList);
                                    }
                                }
                            }

                            if (ObjectUtil.isNotEmpty(recommendList)) {
                                AppletReserveRecipeTypeVO recommend = new AppletReserveRecipeTypeVO();
                                recommend.setTypeId(999L);
                                recommend.setTypeName("推荐");
                                recommend.setDishesList(recommendList);
                                typeList.add(0, recommend);
                            }
                        }
                    }
                }
            }
        }
    }
    public void generateRecipe(Long recipeId, LocalDate applyDate) {
        if (!ObjectUtil.isEmpty(recipeId) && !ObjectUtil.isEmpty(applyDate)) {
            this.generateRecipe(Collections.singletonList(recipeId), applyDate);
        }
    }
    @Transactional(rollbackFor = {Exception.class})
    public synchronized void generateRecipe(List<Long> recipeIdList, LocalDate applyDate) {
        if (!ObjectUtil.isEmpty(recipeIdList) && !ObjectUtil.isEmpty(applyDate)) {
            recipeIdList = new ArrayList(recipeIdList);
            Map<Long, Long> recipeDetailContMap = (Map)this.menuRecipeDetailService.list((Wrapper<MenuRecipeDetail>)((LambdaQueryWrapper<MenuRecipeDetail>)Wrappers.lambdaQuery(MenuRecipeDetail.class).select(MenuRecipeDetail::getRecipeId).eq(MenuRecipeDetail::getApplyDate, applyDate)).in(MenuRecipeDetail::getRecipeId, recipeIdList)).stream().collect(Collectors.groupingBy(MenuRecipeDetail::getRecipeId, Collectors.counting()));
            recipeIdList.removeAll(recipeDetailContMap.keySet());
            if (!ObjectUtil.isEmpty(recipeIdList)) {
                List<MenuRecipe> menuRecipes = this.list((Wrapper)((LambdaQueryWrapper)Wrappers.lambdaQuery(MenuRecipe.class).in(MenuRecipe::getRecipeType, new Object[]{2, 3})).apply(CollUtil.isNotEmpty(recipeIdList),"recipe_id IN (" + recipeIdList.stream().map(String::valueOf).collect(Collectors.joining(", ")) + ")"));
                if (!ObjectUtil.isEmpty(menuRecipes)) {
                    Map<Long, MenuRecipe> recipeMap = (Map)menuRecipes.stream().collect(Collectors.toMap(MenuRecipe::getRecipeId, Function.identity()));
                    List<MenuRecipeDetail> menuRecipeDetails = this.menuRecipeDetailService.list((Wrapper)((LambdaQueryWrapper)Wrappers.lambdaQuery(MenuRecipeDetail.class).eq(MenuRecipeDetail::getDetailType, 1)).apply(CollUtil.isNotEmpty(recipeIdList),"recipe_id IN (" + recipeIdList.stream().map(String::valueOf).collect(Collectors.joining(", ")) + ")"));
                    Map<Long, Integer> recipeTypeMap = (Map)menuRecipes.stream().collect(Collectors.toMap(MenuRecipe::getRecipeId, MenuRecipe::getRecipeType));
                    menuRecipeDetails = this.filterMenuRecipeDetails(recipeTypeMap, menuRecipeDetails, applyDate);
                    if (!ObjectUtil.isEmpty(menuRecipeDetails)) {
                        Map<Long, List<MenuRecipeDetail>> detailMap = (Map)menuRecipeDetails.stream().collect(Collectors.groupingBy(MenuRecipeDetail::getRecipeId));
                        List<Long> detailIds = (List)menuRecipeDetails.stream().map(MenuRecipeDetail::getDetailId).collect(Collectors.toList());
                        Map<Long, List<MenuRecipeDishes>> recipeDishes = (Map)this.menuRecipeDishesService.list((Wrapper)Wrappers.lambdaQuery(MenuRecipeDishes.class).in(MenuRecipeDishes::getDetailId, detailIds)).stream().collect(Collectors.groupingBy(MenuRecipeDishes::getDetailId));
                        List<MenuRecipeDetail> recipeDetailV2List = Lists.newArrayList();
                        List<MenuRecipeDishes> recipeDishesV2List = Lists.newArrayList();
                        Iterator var13 = recipeMap.keySet().iterator();

                        label59:
                        while(true) {
                            Long recipeId;
                            Integer type;
                            List menuRecipeDetail;
                            do {
                                if (!var13.hasNext()) {
                                    this.menuRecipeDetailService.saveBatch(recipeDetailV2List);
                                    if (ObjectUtil.isNotEmpty(recipeDishesV2List)) {
                                        this.menuRecipeDishesService.saveBatch(recipeDishesV2List);
                                    }
                                    return;
                                }
                                recipeId = (Long)var13.next();
                                type = (Integer)recipeTypeMap.get(recipeId);
                                menuRecipeDetail = (List)detailMap.get(recipeId);
                            } while(!ObjectUtil.isNotEmpty(menuRecipeDetail));
                            Iterator var17 = menuRecipeDetail.iterator();
                            while(true) {
                                Long detailId;
                                List dishes;
                                do {
                                    if (!var17.hasNext()) {
                                        continue label59;
                                    }
                                    MenuRecipeDetail recipeDetail = (MenuRecipeDetail)var17.next();
                                    MenuRecipeDetail detailItem = new MenuRecipeDetail();
                                    detailId = Id.next();
                                    detailItem.setDetailId(detailId);
                                    detailItem.setRecipeId(recipeId);
                                    if (ObjectUtil.equal(type, 3)) {
                                        detailItem.setApplyWeek(applyDate.getDayOfWeek().getValue());
                                    }
                                    detailItem.setApplyDate(applyDate);
                                    detailItem.setMealtimeType(recipeDetail.getMealtimeType());
                                    detailItem.setCrby("");
                                    detailItem.setDetailType(2);
                                    recipeDetailV2List.add(detailItem);
                                    dishes = (List)recipeDishes.get(recipeDetail.getDetailId());
                                } while(!ObjectUtil.isNotEmpty(dishes));
                                Iterator var22 = dishes.iterator();
                                while(var22.hasNext()) {
                                    MenuRecipeDishes dish = (MenuRecipeDishes)var22.next();
                                    MenuRecipeDishes dishesItem = (MenuRecipeDishes)BeanUtil.copyProperties(dish, MenuRecipeDishes.class, new String[0]);
                                    dishesItem.setDetailId(detailId);
                                    dishesItem.setId((Long)null);
                                    recipeDishesV2List.add(dishesItem);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    public List<MenuRecipeDetail> filterMenuRecipeDetails(Map<Long, Integer> recipeTypeMap, List<MenuRecipeDetail> menuRecipeDetails, LocalDate applyDate) {
        return (List)menuRecipeDetails.stream().filter((item) -> {
            Long recipeId = item.getRecipeId();
            Integer type = (Integer)recipeTypeMap.get(recipeId);
            if (type == 3) {
                return ObjectUtil.equal(item.getApplyWeek(), applyDate.getDayOfWeek().getValue());
            } else {
                return type == 2;
            }
        }).collect(Collectors.toList());
    }

}
