package com.bonus.canteen.core.cook.service.impl;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.bonus.canteen.core.android.vo.CookMealtimeTypeEnum;
import com.bonus.canteen.core.common.utils.DateUtil;
import com.bonus.canteen.core.common.utils.MqUtil;
import com.bonus.canteen.core.common.utils.RedisUtil;
import com.bonus.canteen.core.cook.domain.CookAppRecipe;
import com.bonus.canteen.core.cook.domain.CookRecipeDetail;
import com.bonus.canteen.core.cook.domain.CookRecipeDishes;
import com.bonus.canteen.core.cook.dto.*;
import com.bonus.canteen.core.cook.enums.CookBindTypeEnum;
import com.bonus.canteen.core.cook.enums.CookRecipeSortEnum;
import com.bonus.canteen.core.cook.enums.RecipeDetailTypeEnum;
import com.bonus.canteen.core.cook.enums.RecipeTypeEnum;
import com.bonus.canteen.core.cook.mapper.CookRecipeDetailMapper;
import com.bonus.canteen.core.cook.mapper.CookRecipeDishesMapper;
import com.bonus.canteen.core.cook.utils.CookRecipeImportListener;
import com.bonus.canteen.core.cook.vo.*;
import com.bonus.canteen.core.user.domain.DeviceMqPersonalUpdateMessageDTO;
import com.bonus.common.core.exception.ServiceException;
import com.bonus.common.core.utils.DateUtils;
import com.bonus.common.houqin.constant.GlobalConstants;
import com.bonus.common.houqin.mq.constant.LeMqConstant;
import com.bonus.common.houqin.utils.LeBeanUtil;
import com.bonus.common.security.utils.SecurityUtils;
import com.google.common.collect.Lists;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.bonus.canteen.core.cook.mapper.CookRecipeMapper;
import com.bonus.canteen.core.cook.domain.CookRecipe;
import com.bonus.canteen.core.cook.service.ICookRecipeService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

/**
 * 菜品计划信息Service业务层处理
 * 
 * @author xsheng
 * @date 2025-05-25
 */
@Service
public class CookRecipeServiceImpl implements ICookRecipeService {
    @Autowired
    private CookRecipeMapper cookRecipeMapper;
    @Autowired
    private CookRecipeDetailMapper cookRecipeDetailMapper;
    @Autowired
    private CookRecipeDishesMapper cookRecipeDishesMapper;

    /**
     * 查询菜品计划信息
     * 
     * @param recipeId 菜品计划信息主键
     * @return 菜品计划信息
     */
    @Override
    public CookRecipe selectCookRecipeByRecipeId(Long recipeId) {
        return cookRecipeMapper.selectCookRecipeByRecipeId(recipeId);
    }

    /**
     * 查询菜品计划信息列表
     * 
     * @param cookRecipe 菜品计划信息
     * @return 菜品计划信息
     */
    @Override
    public List<CookRecipe> selectCookRecipeList(CookRecipe cookRecipe) {
        List<CookRecipe> list = cookRecipeMapper.selectCookRecipeList(cookRecipe);
//        for (CookRecipe recipe : list) {
//            if (1 == recipe.getRecipeType()) {
//                recipe.setDishesCount(cookRecipeMapper.getDishesCount4PointDates(recipe.getRecipeId()));
//            } else {
//                recipe.setDishesCount(cookRecipeMapper.getDishesCount4Recycle(recipe.getRecipeId()));
//            }
//        }
        List<LocalDate> lastWeekDate = DateUtil.getLastWeekDate();
        List<Long> recipeIds = list.stream().map(CookRecipe::getRecipeId).collect(Collectors.toList());
        List<CookRecipeDishesCountVO> dishesCountByRecipeIdsList = cookRecipeMapper.getDishesCountByRecipeIds(recipeIds);
        Map<Long, Integer> dishesCountByRecipeIds = dishesCountByRecipeIdsList.stream().collect(Collectors.toMap(CookRecipeDishesCountVO::getRecipeId, CookRecipeDishesCountVO::getCount));
        List<CookRecipeDetailVO> recipeDatesList = this.cookRecipeMapper.selectApplyDateListByRecipeId(LocalDate.now(), recipeIds);
        Map<Long, TreeSet<LocalDate>> recipeDates = recipeDatesList.stream().collect(Collectors.groupingBy(CookRecipeDetailVO::getRecipeId, Collectors.mapping(CookRecipeDetailVO::getApplyDate, Collectors.toCollection(TreeSet::new))));
        if (list.isEmpty()) {
            return new ArrayList<>();
        }
        list.forEach(r -> {
            Long recipeId = r.getRecipeId();
            r.setDishesCount(dishesCountByRecipeIds.getOrDefault(recipeId, 0));
            Set<LocalDate> applyDateList = recipeDates.getOrDefault(recipeId, new TreeSet<>());
            Object finalDate;
            if (ObjectUtil.equal(r.getRecipeType(), 3)) {
                Stream<LocalDate> localDateStream = lastWeekDate.stream();
                Objects.requireNonNull(applyDateList);
                finalDate = localDateStream.filter(applyDateList::contains).collect(Collectors.toList());
            } else if (ObjectUtil.equal(r.getRecipeType(), 2)) {
                finalDate = DateUtil.getLastWeek();
            } else {
                finalDate = new ArrayList<>(applyDateList);
            }
            List<String> applyDateStringList = Lists.newArrayList();
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("MM-dd");
            ((List) finalDate).forEach(d -> {
                applyDateStringList.add(dateTimeFormatter.format((TemporalAccessor) d));
            });
            r.setApplyDateList((List) finalDate);
            r.setApplyDateStringList(applyDateStringList);
        });
        return list;
    }

//    public List<CookRecipeVO> selectCookRecipeList4Current(CookRecipeDTO content) {
//        Map<Integer, CookRecipeSortEnum> bindTypeMap = CookRecipeSortEnum.getKeyMap();
//        CookRecipeSortEnum sortEnum = bindTypeMap.get(content.getKey());
//        List<CookRecipeVO> records = new ArrayList<>();
//        if (Objects.isNull(sortEnum)) {
//            throw new ServiceException("参数错误");
//        }else{
//            content.setDeviceTypeList(sortEnum.getDeviceType());
//            content.setBindType(sortEnum.getBindType());
//            content.setRecipeName(LeBeanUtil.fieldLikeHandle(content.getRecipeName()));
//            records = cookRecipeMapper.selectCookRecipeListV2(content);
//            if (!CollectionUtils.isEmpty(records)) {
//                List<Long> recipeIds;
//                Map<Long ,Long> maping;
//                List<Long> stallIds = records.stream().map(CookRecipeVO::getStallId).collect(Collectors.toList());
//                List<StallAndRecipeBindVO> stallAndRecipeBindDtos = this.cookRecipeMapper.selectRecipeByStallIdsAndBindType(stallIds, content.getBindType());
//                maping = stallAndRecipeBindDtos.stream().collect(Collectors.toMap(StallAndRecipeBindVO::getStallId, StallAndRecipeBindVO::getRecipeId));
//                recipeIds = stallAndRecipeBindDtos.stream().map(StallAndRecipeBindVO::getRecipeId).distinct().collect(Collectors.toList());
////                this.generateRecipe(recipeIds, content.getApplyDate());
//                List<CookRecipeDetailVO> cookRecipeDetailVOs = this.cookRecipeMapper.selectTmpBetweenDateDetailList(recipeIds, content.getApplyDate(), content.getApplyDate());
//                Map<Long, CookRecipe> recipeMap = (recipeIds == null || recipeIds.isEmpty())
//                        ? new HashMap<>()
//                        : this.cookRecipeMapper.selectRecipeListByRecipeIds(recipeIds)
//                        .stream()
//                        .collect(Collectors.toMap(CookRecipe::getRecipeId, Function.identity()));
//                Map<Long, CookRecipeDetailVO> detailMap = cookRecipeDetailVOs.stream().collect(Collectors.toMap(CookRecipeDetailVO::getRecipeId, Function.identity(), (t1, t2) -> {
//                    return t1;
//                }));
//                System.err.println("records = " + records.size());
//                CookRecipeVO item;
//                long count;
//                for(Iterator<CookRecipeVO> var12 = records.iterator(); var12.hasNext(); item.setDishesCount(count)) {
//                    item = var12.next();
//                    Long recipeId;
//                    recipeId = maping.get(item.getStallId());
//                    item.setRecipeId(recipeId);
//                    CookRecipeDateVO cookRecipeDateVO = new CookRecipeDateVO();
//                    cookRecipeDateVO.setDetailList(Arrays.asList(detailMap.get(recipeId)));
//                    item.setRecipeDateList(Arrays.asList(cookRecipeDateVO));
//                    item.setRecipeName(Optional.ofNullable(recipeMap.get(recipeId)).map(CookRecipe::getRecipeName).orElse(null));
//                    count = 0L;
//                    if (Objects.nonNull(item.getRecipeDateDetail()) && !CollectionUtils.isEmpty(item.getRecipeDateList().get(0).getDetailList())) {
//                        count = item.getRecipeDateList().get(0).getDetailList().stream().filter((x) -> {
//                            return !CollectionUtils.isEmpty(x.getDishesList());
//                        }).flatMap((x) -> {
//                            return x.getDishesList().stream();
//                        }).map(CookRecipeDishesVO::getDishesId).distinct().count();
//                    }
//                }
//            }
//        }
//        return records;
//    }

    @Override
    public List<AllocRecipeStallVO> selectCookRecipeList4Current(AllocRecipeStallDTO content) {
        Map<Integer, CookRecipeSortEnum> bindTypeMap = CookRecipeSortEnum.getKeyMap();
        CookRecipeSortEnum sortEnum = bindTypeMap.get(content.getKey());
        List<AllocRecipeStallVO> records = new ArrayList<>();
        if (ObjectUtil.isEmpty(sortEnum)) {
            throw new ServiceException("参数错误");
        }else{
            content.setDeviceTypeList(sortEnum.getDeviceType());
            content.setBindType(sortEnum.getBindType());
            content.setRecipeName(LeBeanUtil.fieldLikeHandle(content.getRecipeName()));
            records = cookRecipeMapper.selectCookRecipeListV2(content);
            if (ObjectUtil.isNotEmpty(records)) {
                List<Long> recipeIds;
                Map<Long ,Long> maping;
                List<Long> menuRecipeDateVOS;
                menuRecipeDateVOS = records.stream().map(AllocRecipeStallVO::getStallId).collect(Collectors.toList());
                List<StallAndRecipeBindDto> stallAndRecipeBindDtos = this.cookRecipeMapper.selectRecipeByStallIdsAndBindType(menuRecipeDateVOS, content.getBindType());
                maping = stallAndRecipeBindDtos.stream().collect(Collectors.toMap(StallAndRecipeBindDto::getStallId, StallAndRecipeBindDto::getRecipeId));
                recipeIds = stallAndRecipeBindDtos.stream().map(StallAndRecipeBindDto::getRecipeId).distinct().collect(Collectors.toList());

                //this.generateRecipe(recipeIds, content.getApplyDate());
                List<CookRecipeDateVO> menuRecipeDateVOS1 = this.cookRecipeMapper.selectTmpBetweenDateDetailList(recipeIds, content.getApplyDate(), content.getApplyDate());
                Map<Long, CookRecipe> recipeMap = (recipeIds == null || recipeIds.isEmpty())
                        ? new HashMap<>()
                        : this.cookRecipeMapper.selectRecipeListByRecipeIds(recipeIds)
                        .stream()
                        .collect(Collectors.toMap(CookRecipe::getRecipeId, Function.identity()));
                Map<Long, CookRecipeDateVO> detailMap = menuRecipeDateVOS1.stream().collect(Collectors.toMap(CookRecipeDateVO::getRecipeId, Function.identity(), (t1, t2) -> {
                    return t1;
                }));
                System.err.println("records = " + records.size());
                AllocRecipeStallVO item;
                long count;
                for(Iterator<AllocRecipeStallVO> var12 = records.iterator(); var12.hasNext(); item.setDishesCount(count)) {
                    item = var12.next();
                    Long recipeId;
                    recipeId = maping.get(item.getStallId());

                    item.setRecipeId(recipeId);
                    item.setDetail(detailMap.get(recipeId));
                    item.setRecipeName(Optional.ofNullable(recipeMap.get(recipeId)).map(CookRecipe::getRecipeName).orElse(null));
                    // item.setUptime(Optional.ofNullable(recipeMap.get(recipeId)).map(MenuRecipe::getUptime).orElse(null));
                    count = 0L;
                    if (ObjectUtil.isNotEmpty(item.getDetail()) && ObjectUtil.isNotEmpty(item.getDetail().getDetailList())) {
                        count = item.getDetail().getDetailList().stream().filter((x) -> {
                            return ObjectUtil.isNotEmpty(x.getDishesList());
                        }).flatMap((x) -> {
                            return x.getDishesList().stream();
                        }).map(CookRecipeDishesVO::getDishesId).distinct().count();
                    }
                }
            }
        }
        return records;
    }

    @Override
    public List<CookRecipeDetailVO> getRecipeDetailByDate(CookRecipeDetailDTO dto) {
        List<CookRecipeDetailVO> menuRecipeDetails = cookRecipeMapper.selectRecipeDetailList(dto.getRecipeId(), dto.getApplyDate());
        return menuRecipeDetails.stream().sorted(Comparator.comparing(CookRecipeDetailVO::getMealtimeType)).collect(Collectors.toList());
    }

    @Override
    public List<CookRecipeDetailVO> getRecipeDetail(CookRecipeDetailDTO dto) {
        return cookRecipeMapper.getRecipeDetail(dto);
    }

    @Override
    public List<CookRecipeDetailVO> selectTemplateDetailList(CookRecipeDetailDTO dto) {
        return cookRecipeMapper.selectTemplateDetailList(dto);
    }

    /**
     * 新增菜品计划信息
     * 
     * @param cookRecipeDTO 菜品计划信息
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertCookRecipe(CookRecipeDTO cookRecipeDTO) {
        cookRecipeDTO.setCreateTime(DateUtils.getNowDate());
        cookRecipeDTO.setCreateBy(SecurityUtils.getUsername());
        try {
            CookRecipe cookRecipe = cookRecipeMapper.selectCookRecipeByRecipeName(cookRecipeDTO.getRecipeName());
            if (Objects.nonNull(cookRecipe)) {
                throw new ServiceException("该菜谱名称已存在，请重新输入");
            }
            int count = cookRecipeMapper.insertCookRecipe(cookRecipeDTO); //插入菜谱
            createRecipeDetails(cookRecipeDTO); //创建详情+菜品数据
            return count;
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * 修改菜品计划信息
     *
     * @param cookRecipeDTO 菜品计划信息
     * @return 结果
     */
    @Override
    public int updateCookRecipe(CookRecipeDTO cookRecipeDTO) {
        cookRecipeDTO.setUpdateTime(DateUtils.getNowDate());
        cookRecipeDTO.setUpdateBy(SecurityUtils.getUsername());
        try {
            List<CookRecipe> cookRecipes = cookRecipeMapper.selectCookRecipeList(new CookRecipe());
            List<String> otherCookRecipes = cookRecipes.stream().filter(item -> !item.getRecipeId().equals(cookRecipeDTO.getRecipeId()))
                    .map(CookRecipe::getRecipeName).collect(Collectors.toList());
            if (otherCookRecipes.contains(cookRecipeDTO.getRecipeName())) {
                throw new ServiceException("该菜谱名称已存在，请重新输入");
            }
            int count = cookRecipeMapper.updateCookRecipe(cookRecipeDTO);
            // 删除旧的详情+菜品数据
            deleteOldCookRecipeDetailsAndDishes(cookRecipeDTO);
            // 重新创建详情+菜品数据
            createRecipeDetails(cookRecipeDTO);
            // 发送mq
            List<Integer> deviceIds = this.cookRecipeMapper.getDeviceIdByDevice(String.valueOf(cookRecipeDTO.getCanteenId()), String.valueOf(cookRecipeDTO.getStallId()));
            if(deviceIds !=null && deviceIds.size() >0){
                List<String> deviceSnList = this.cookRecipeMapper.getDeviceSnByIds(deviceIds);
                for (String deviceSn : deviceSnList){
                    DeviceMqPersonalUpdateMessageDTO bean = new DeviceMqPersonalUpdateMessageDTO().setUpdatePerson(0,"update");
                    MqUtil.pushToSingleDevice(bean, LeMqConstant.Topic.DEVICE_UPDATE_MENU_CONFIG_V4, deviceSn);
                }
            }
            return count;
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
    }

    private void createRecipeDetails(CookRecipeDTO cookRecipeDTO) {
        // 插入指定日期、日循环摸板、周循环模板
        if (!CollectionUtils.isEmpty(cookRecipeDTO.getRecipeDateList())) {
            for (CookRecipeDateDTO recipeDateDTO : cookRecipeDTO.getRecipeDateList()) {
                List<CookRecipeDetailDTO> detailList = recipeDateDTO.getDetailList();
                if (!CollectionUtils.isEmpty(detailList)) {
                    for (CookRecipeDetailDTO detailDTO : detailList) {
                        if (CollectionUtils.isEmpty(detailDTO.getDishesList())) { //不插入空数据
                            continue;
                        }
                        insertDetailAndDishes(cookRecipeDTO, recipeDateDTO, detailDTO);
                    }
                }
            }
        }
        // 插入生成的日循环详情数据、周循环详情数据
        List<CookRecipeDateDTO> generatedRecipeDateList = generateCookRecipeDateList(cookRecipeDTO);
        if (!CollectionUtils.isEmpty(generatedRecipeDateList)) {
            for (CookRecipeDateDTO recipeDateDTO : generatedRecipeDateList) {
                List<CookRecipeDetailDTO> detailList = recipeDateDTO.getDetailList();
                if (!CollectionUtils.isEmpty(detailList)) {
                    for (CookRecipeDetailDTO detailDTO : detailList) {
                        if (CollectionUtils.isEmpty(detailDTO.getDishesList())) { //不插入空数据
                            continue;
                        }
                        insertDetailAndDishes(cookRecipeDTO, recipeDateDTO, detailDTO);
                    }
                }
            }
        }
    }

    private static List<CookRecipeDateDTO> generateCookRecipeDateList(CookRecipeDTO cookRecipeDTO) {
        List<CookRecipeDateDTO> recipeDateList = cookRecipeDTO.getRecipeDateList();
        if (CollectionUtils.isEmpty(recipeDateList)) {
            return null;
        }
        List<CookRecipeDateDTO> recipeDateListNew = new ArrayList<>();
        if (RecipeTypeEnum.DAILY.key().equals(cookRecipeDTO.getRecipeType()) && Objects.nonNull(recipeDateList.get(0))) {  //每日循环
            List<CookRecipeDetailDTO> recipeDetailList = recipeDateList.get(0).getDetailList();
            LocalDate now = LocalDate.now();
            for (int i = 0; i < GlobalConstants.WEEK_DAYS; ++i) {
                CookRecipeDateDTO cookRecipeDateDTO = new CookRecipeDateDTO();
                cookRecipeDateDTO.setDetailList(recipeDetailList);
                cookRecipeDateDTO.setApplyDate(now.plusDays(i));
                recipeDateListNew.add(cookRecipeDateDTO);
            }
        } else if (RecipeTypeEnum.WEEKLY.key().equals(cookRecipeDTO.getRecipeType())) {  //每周循环
            HashMap<Integer, LocalDate> dateHashMap = DateUtil.getDaysInWeekMap();
            Iterator<Map.Entry<Integer, LocalDate>> iterator = dateHashMap.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<Integer, LocalDate> entry = iterator.next();
                CookRecipeDateDTO cookRecipeDateDTO = new CookRecipeDateDTO();
                cookRecipeDateDTO.setApplyWeek(Long.valueOf(entry.getKey()));
                cookRecipeDateDTO.setApplyDate(entry.getValue());
                for (CookRecipeDateDTO recipeDateDTO : recipeDateList) {
                    if (Long.valueOf(entry.getKey()).equals(recipeDateDTO.getApplyWeek())) {
                        cookRecipeDateDTO.setDetailList(recipeDateDTO.getDetailList());
                    }
                }
                recipeDateListNew.add(cookRecipeDateDTO);
            }
        }
        return recipeDateListNew;
    }

    private void insertDetailAndDishes(CookRecipeDTO cookRecipeDTO, CookRecipeDateDTO recipeDateDTO, CookRecipeDetailDTO detailDTO) {
        CookRecipeDetail cookRecipeDetail = new CookRecipeDetail();
        cookRecipeDetail.setRecipeDetailId(detailDTO.getRecipeDetailId());
        cookRecipeDetail.setMealtimeType(detailDTO.getMealtimeType());
        cookRecipeDetail.setSaleTypeId(detailDTO.getSaleTypeId());
        cookRecipeDetail.setApplyDate(recipeDateDTO.getApplyDate());
        cookRecipeDetail.setApplyWeek(recipeDateDTO.getApplyWeek());
        cookRecipeDetail.setApplyMonth(detailDTO.getApplyMonth());
        cookRecipeDetail.setRecipeId(cookRecipeDTO.getRecipeId());
        cookRecipeDetail.setCreateBy(SecurityUtils.getUsername());
        cookRecipeDetail.setCreateTime(DateUtils.getNowDate());
        if (Objects.isNull(cookRecipeDetail.getApplyDate())) {
            cookRecipeDetail.setDetailType(RecipeDetailTypeEnum.TEMPLATE_DATA.key()); //模板数据
        } else {
            cookRecipeDetail.setDetailType(RecipeDetailTypeEnum.DETAIL_DATA.key()); //详情数据
        }
        cookRecipeDetailMapper.insertCookRecipeDetail(cookRecipeDetail); //插入菜谱之执行计划
        List<CookRecipeDishesDTO> dishesList = detailDTO.getDishesList();
        for (CookRecipeDishesDTO dishesDTO : dishesList) {
            CookRecipeDishes cookRecipeDishes = new CookRecipeDishes();
            BeanUtils.copyProperties(dishesDTO, cookRecipeDishes);
            cookRecipeDishes.setCreateTime(DateUtils.getNowDate());
            long supplyNum = Objects.isNull(cookRecipeDishes.getSupplyNum()) ? 0 : cookRecipeDishes.getSupplyNum();
            long saleNum = Objects.isNull(cookRecipeDishes.getSaleNum()) ? 0 : cookRecipeDishes.getSaleNum();
            cookRecipeDishes.setRemanentNum((int) (supplyNum - saleNum));
            cookRecipeDishes.setRecipeDetailId(cookRecipeDetail.getRecipeDetailId());
            cookRecipeDishesMapper.insertCookRecipeDishes(cookRecipeDishes);  //插入菜谱之菜品详情
        }
    }

    private void deleteOldCookRecipeDetailsAndDishes(CookRecipeDTO cookRecipeDTO) {
        List<Long> cookRecipeDetailIds = new ArrayList<>();
        // 更新菜谱时，指定日期菜谱，删掉指定日期的菜谱
        if (RecipeTypeEnum.POINT_DATE.key().equals(cookRecipeDTO.getRecipeType())) {
            List<LocalDate> dateList = cookRecipeDTO.getRecipeDateList().stream().map(CookRecipeDateDTO::getApplyDate).collect(Collectors.toList());
            cookRecipeDetailIds = cookRecipeDetailMapper.getToDeleteCookRecipeDetailIds4PointDays(cookRecipeDTO.getRecipeId(), dateList);
        }
        // 循环日菜谱，删掉模板数据 + 今日开始的详情数据
        if (RecipeTypeEnum.DAILY.key().equals(cookRecipeDTO.getRecipeType())) {
            cookRecipeDetailIds = cookRecipeDetailMapper.getToDeleteCookRecipeDetailIds4Daily(cookRecipeDTO.getRecipeId());
        }
        // 循环周菜谱，删掉指定周x模板数据 + 今日开始的周x的详情数据
        if (RecipeTypeEnum.WEEKLY.key().equals(cookRecipeDTO.getRecipeType())) {
            List<Long> applyWeeks = cookRecipeDTO.getRecipeDateList().stream().map(CookRecipeDateDTO::getApplyWeek).collect(Collectors.toList());
            HashMap<Integer, LocalDate> dateHashMap = DateUtil.getDaysInWeekMap();
            List<LocalDate> dateList = new ArrayList<>();
            for (Long applyWeek : applyWeeks) {
                dateList.add(dateHashMap.get(applyWeek.intValue()));
            }
            //模板数据
            cookRecipeDetailIds.addAll(cookRecipeDetailMapper.getToDeleteCookRecipeDetailIds4WeeklyTemplate(cookRecipeDTO.getRecipeId(), applyWeeks));
            //详情数据
            cookRecipeDetailIds.addAll(cookRecipeDetailMapper.getToDeleteCookRecipeDetailIds4WeeklyDetail(cookRecipeDTO.getRecipeId(), dateList));
        }
        if (!CollectionUtils.isEmpty(cookRecipeDetailIds)) {
            Long[] cookRecipeDetailArray = cookRecipeDetailIds.stream().toArray(Long[]::new);
            cookRecipeDishesMapper.deleteCookRecipeDishesByCookRecipeDetailIds(cookRecipeDetailArray);
            cookRecipeDetailMapper.deleteCookRecipeDetailByRecipeDetailIds(cookRecipeDetailArray);
        }
    }

    @Transactional(rollbackFor = {Exception.class})
    public void bindCookRecipe(CookRecipeBindDTO content) {
        CookRecipe cookRecipe = this.cookRecipeMapper.selectCookRecipeByRecipeId(content.getRecipeId());
        CookRecipeSortEnum sortEnum = CookRecipeSortEnum.getBindTypeMap().get(content.getBindType());
        if (Objects.isNull(sortEnum)) {
            throw new ServiceException("菜谱绑定类型错误");
        } else {
            List<Integer> deviceIds = new ArrayList<>();
            if (CookBindTypeEnum.TABLE.key().equals(content.getBindType())) { //TABLE(7, "智慧餐台/消费机")
                if (content.getStallId() != null) {
                    deviceIds = this.cookRecipeMapper.getDeviceIdByDevice(null, String.valueOf(content.getStallId()));
                    if (deviceIds == null || deviceIds.isEmpty() || deviceIds.size() == 0) {
                        throw new ServiceException("该食堂档口下没有设备信息");
                    }
                }
            }
            CookAppRecipe appRecipe = new CookAppRecipe();
            appRecipe.setRecipeId(content.getRecipeId());
            appRecipe.setMealLineId(content.getMealLineId());
            appRecipe.setBindType(content.getBindType());
            appRecipe.setBindTime(LocalDateTime.now());
            if (content.getHandleType().equals(1)) {
//                if (CookBindTypeEnum.RESERVE.key().equals(content.getBindType())) {
//                    exists = this.allocCanteenApi.checkCanteenIfReserve(cookRecipe.getCanteenId(), cookRecipe.getStallId());
//                    if (!exists) {
//                        throw new ServiceException("不支持预订餐");
//                    }
//                }
                boolean exists = this.cookRecipeMapper.checkIfAlreadyBind(appRecipe);
                if (exists) {
                    throw new ServiceException("菜谱已被使用，请勿重复绑定");
                }
                List<Long> sourceRecipeList = this.cookRecipeMapper.selectRecipeInSameShop(cookRecipe.getStallId(), content.getBindType(), content.getMealLineId());
                if (!CollectionUtils.isEmpty(sourceRecipeList)) {
                    appRecipe.setRecipeIds(sourceRecipeList);
                    this.cookRecipeMapper.deleteBind(appRecipe);
                }
                this.cookRecipeMapper.insertBind(appRecipe);
                if (!CollectionUtils.isEmpty(deviceIds)) {
                    this.cookRecipeMapper.updateRecipeByCanteenStallMeal(content.getRecipeId(), cookRecipe.getCanteenId(), cookRecipe.getStallId(), deviceIds);
                }
                MqUtil.sendDataChange(cookRecipe, LeMqConstant.DataChangeType.ADD, LeMqConstant.Topic.DATA_CHANGE_RECIPE_RELEASE);
            } else {
                appRecipe.setRecipeIds(Collections.singletonList(appRecipe.getRecipeId()));
                this.cookRecipeMapper.deleteBind(appRecipe);
                if (!CollectionUtils.isEmpty(deviceIds)) {
                    this.cookRecipeMapper.updateRecipeByCanteenStallMeal(-1L,  cookRecipe.getCanteenId(), cookRecipe.getStallId(), deviceIds);
                }
            }
            //发送mq
            if (CookBindTypeEnum.TABLE.key().equals(content.getBindType())) { //TABLE(7, "智慧餐台/消费机")
                List<String> deviceSnList = this.cookRecipeMapper.getDeviceSnByIds(deviceIds);
                for (String deviceSn : deviceSnList) {
                    DeviceMqPersonalUpdateMessageDTO bean = new DeviceMqPersonalUpdateMessageDTO().setUpdatePerson(0, content.getHandleType().equals(1) ? "update" : "del");
                    MqUtil.pushToSingleDevice(bean, LeMqConstant.Topic.DEVICE_UPDATE_MENU_CONFIG_V4, deviceSn);
                }
            }
        }
    }

    @Override
    public Set<String> recipeImportDishes(MultipartFile excel, Long recipeId) {
        ExcelReader excelReader = null;
        try {
            Set<String> notImportDishesNames = new HashSet();
            List<CookRecipeImportListener> listeners = new ArrayList();

            excelReader = EasyExcel.read(excel.getInputStream()).build();
            List<ReadSheet> readSheets = excelReader.excelExecutor().sheetList();
            for (ReadSheet readSheet : readSheets) {
                String sheetName = readSheet.getSheetName();
                Integer mealtimeType = CookMealtimeTypeEnum.getKeyByDesc(sheetName);
                if (!Objects.isNull(mealtimeType)) {
                    CookRecipeImportListener cookRecipeImportListener = new CookRecipeImportListener(sheetName, recipeId, mealtimeType.longValue());
                    listeners.add(cookRecipeImportListener);
                    readSheet.getCustomReadListenerList().add(cookRecipeImportListener);
                }
            }
            excelReader.read(readSheets);
            notImportDishesNames.addAll(
                    listeners.stream()
                            .flatMap(listener -> listener.getNotImportDishesNames().stream())
                            .collect(Collectors.toList())
            );

            return notImportDishesNames;
        }catch (Exception e) {
            throw new ServiceException(e.toString());
        } finally {
            if (excelReader != null) {
                excelReader.finish();
            }

        }
    }

    /**
     * 批量删除菜品计划信息
     * 
     * @param recipeIds 需要删除的菜品计划信息主键
     * @return 结果
     */
    @Override
    public int deleteCookRecipeByRecipeIds(Long[] recipeIds) {
        List<Long> cookRecipeDetailIds = cookRecipeDetailMapper.getAllCookRecipeDetailIds(recipeIds);
        Long[] cookRecipeDetailArray = cookRecipeDetailIds.stream().toArray(Long[]::new);
        cookRecipeDishesMapper.deleteCookRecipeDishesByCookRecipeDetailIds(cookRecipeDetailArray);
        cookRecipeDetailMapper.deleteCookRecipeDetailByRecipeDetailIds(cookRecipeDetailArray);
        int count = cookRecipeMapper.deleteCookRecipeByRecipeIds(recipeIds);
        for (int i = 0; i < recipeIds.length; i++) {
            String redisKey = String.format("yst:%s:recipe:%s:detail:%s", GlobalConstants.TENANT_ID, recipeIds[i], "*");
            RedisUtil.deleteByPattern(redisKey);
            MqUtil.sendDataChange(recipeIds[i], LeMqConstant.DataChangeType.REMOVE, LeMqConstant.Topic.DATA_CHANGE_RECIPE);
        }
        return count;
    }

    /**
     * 删除菜品计划信息信息
     * 
     * @param recipeId 菜品计划信息主键
     * @return 结果
     */
    @Override
    public int deleteCookRecipeByRecipeId(Long recipeId) {
        return cookRecipeMapper.deleteCookRecipeByRecipeId(recipeId);
    }
}
