package org.dromara.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.dromara.common.core.constant.BizConst;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.system.domain.SysDiseaseLabel;
import org.dromara.system.domain.SysFoodCategory;
import org.dromara.system.domain.SysFoodIngredient;
import org.dromara.system.domain.SysRecipeCategory;
import org.dromara.system.domain.SysRecipeFoodIngredient;
import org.dromara.system.domain.SysRole;
import org.dromara.system.domain.bo.SysRecipeFoodIngredientBo;
import org.dromara.system.domain.vo.SysDictDataVo;
import org.dromara.system.domain.vo.SysDiseaseLabelVo;
import org.dromara.system.domain.vo.SysFoodCategoryVo;
import org.dromara.system.domain.vo.SysFoodIngredientVo;
import org.dromara.system.domain.vo.SysRecipeFoodIngredientVo;
import org.dromara.system.mapper.SysDiseaseLabelMapper;
import org.dromara.system.mapper.SysFoodCategoryMapper;
import org.dromara.system.mapper.SysFoodIngredientMapper;
import org.dromara.system.mapper.SysRecipeCategoryMapper;
import org.dromara.system.mapper.SysRecipeFoodIngredientMapper;
import org.dromara.system.service.ISysDictDataService;
import org.dromara.system.service.ISysDiseaseLabelService;
import org.dromara.system.service.ISysRecipeCategoryService;
import org.springframework.stereotype.Service;
import org.dromara.system.domain.bo.SysRecipeBo;
import org.dromara.system.domain.vo.SysRecipeVo;
import org.dromara.system.domain.SysRecipe;
import org.dromara.system.mapper.SysRecipeMapper;
import org.dromara.system.service.ISysRecipeService;
import org.springframework.transaction.annotation.Transactional;

import java.io.ObjectInput;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 食谱管理Service业务层处理
 *
 * @author Lion Li
 * @date 2025-07-01
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class SysRecipeServiceImpl implements ISysRecipeService {

    private final SysRecipeMapper baseMapper;
    private final ISysDictDataService dictDataService;
    private final SysRecipeCategoryMapper recipeCategoryMapper;
    private final SysRecipeFoodIngredientMapper sysRecipeFoodIngredientMapper;
    private final SysDiseaseLabelMapper diseaseLabelMapper;
    private final SysFoodCategoryMapper foodCategoryMapper;
    private final SysFoodIngredientMapper foodIngredientMapper;

    @Override
    public int updateRecipeStatus(List<Long> recipeIds, String status) {
        return baseMapper.update(null,
            new LambdaUpdateWrapper<SysRecipe>()
                .set(SysRecipe::getSmartRecommend, status)
                .in(SysRecipe::getRecipeId, recipeIds));
    }


    /**
     * 查询食谱管理
     *
     * @param recipeId 主键
     * @return 食谱管理
     */
    @Override
    public SysRecipeVo queryById(Long recipeId) {
        SysRecipeVo vo = baseMapper.selectVoById(recipeId);
        Set<String> diseaseLabelIdList = CollUtil.newHashSet();
        Map<String, List<SysDictDataVo>> dictDataMap = dictDataService.selectGroupByType(List.of(BizConst.DIETARY_TYPES, BizConst.FOOD_UNIT)).getData();
        Map<String, SysDictDataVo> foodUnitMap = dictDataMap.get(BizConst.FOOD_UNIT).stream().collect(Collectors.toMap(v -> v.getDictValue(), v -> v, (k, v) -> k));
        Map<String, SysDictDataVo> dietaryTypesMap = dictDataMap.get(BizConst.DIETARY_TYPES).stream().collect(Collectors.toMap(v -> v.getDictValue(), v -> v, (k, v) -> k));

        if (StrUtil.isNotBlank(vo.getAvoidDisease())) {
            diseaseLabelIdList.addAll(Arrays.stream(vo.getAvoidDisease().split(",")).collect(Collectors.toList()));
        }
        if (StrUtil.isNotBlank(vo.getSuitableDisease())) {
            diseaseLabelIdList.addAll(Arrays.stream(vo.getSuitableDisease().split(",")).collect(Collectors.toList()));
        }
        if (CollUtil.isNotEmpty(diseaseLabelIdList)) {
            List<SysDiseaseLabel> diseaseLabelList = diseaseLabelMapper.selectList(Wrappers.lambdaQuery(SysDiseaseLabel.class)
                .select(SysDiseaseLabel::getLabelId, SysDiseaseLabel::getLabelName, SysDiseaseLabel::getLabelCode, SysDiseaseLabel::getCategory)
                .in(SysDiseaseLabel::getLabelId, diseaseLabelIdList));

            Map<String, SysDiseaseLabelVo> diseaseLabelMap = diseaseLabelList
                .stream().map(v -> MapstructUtils.convert(v, SysDiseaseLabelVo.class))
                .collect(Collectors.toMap(k -> String.valueOf(k.getLabelId()), v -> v, (k, v) -> k));

            vo.setAvoidDiseaseList(Arrays.stream(vo.getAvoidDisease().split(",")).map(v -> {
                SysDiseaseLabelVo labelVo = diseaseLabelMap.get(v);
                if (ObjUtil.isNotNull(labelVo)) {
                    SysDictDataVo dataVo = dietaryTypesMap.get(labelVo.getCategory());
                    if (ObjUtil.isNotNull(dataVo)) {
                        labelVo.setCategoryName(dataVo.getDictLabel());
                    }
                }
                return labelVo;
            }).collect(Collectors.toList()));

            vo.setSuitableDiseaseList(Arrays.stream(vo.getSuitableDisease().split(",")).map(v -> {
                SysDiseaseLabelVo labelVo = diseaseLabelMap.get(v);
                if (ObjUtil.isNotNull(labelVo)) {
                    SysDictDataVo dataVo = dietaryTypesMap.get(labelVo.getCategory());
                    if (ObjUtil.isNotNull(dataVo)) {
                        labelVo.setCategoryName(dataVo.getDictLabel());
                    }
                }
                return labelVo;
            }).collect(Collectors.toList()));

            List<SysFoodIngredient> foodIngredientList = sysRecipeFoodIngredientMapper.selectRecipeFood(recipeId);
            if (CollUtil.isNotEmpty(foodIngredientList)) {
                Set<String> categoryIdList = CollUtil.newHashSet();
                foodIngredientList.forEach(foodIngredient -> {
                    if (StrUtil.isNotBlank(foodIngredient.getFoodCategoryId())) {
                        categoryIdList.addAll(Arrays.stream(foodIngredient.getFoodCategoryId().split(",")).collect(Collectors.toList()));
                    }
                });

                Map<String, String> foodCategoryMap = foodCategoryMapper.selectList(Wrappers.lambdaQuery(SysFoodCategory.class)
                        .select(SysFoodCategory::getFoodCategoryId, SysFoodCategory::getName)
                        .in(SysFoodCategory::getFoodCategoryId, categoryIdList))
                    .stream().collect(Collectors.toMap(k -> String.valueOf(k.getFoodCategoryId()), v -> v.getName(), (k, v) -> k));

                foodIngredientList.forEach(foodIngredient -> {
                    if (StrUtil.isNotBlank(foodIngredient.getFoodCategoryId())) {
                        Arrays.stream(foodIngredient.getFoodCategoryId().split(",")).forEach(v -> {
                            foodIngredient.setSubCategory(foodCategoryMap.get(v));
                        });
                    }

                    SysDictDataVo dataVo = foodUnitMap.get(foodIngredient.getUnit());
                    if (ObjUtil.isNotNull(dataVo)) {
                        foodIngredient.setUnitName(dataVo.getDictLabel());
                    }
                });
            }
            vo.setFoodList(MapstructUtils.convert(foodIngredientList, SysFoodIngredientVo.class));
        }

        return vo;
    }

    /**
     * 分页查询食谱管理列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 食谱管理分页列表
     */
    @Override
    public TableDataInfo<SysRecipeVo> queryPageList(SysRecipeBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<SysRecipe> lqw = buildQueryWrapper(bo);
        Page<SysRecipeVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);

        if (CollUtil.isNotEmpty(result.getRecords())) {
            Map<String, SysDictDataVo> dietaryTypesMap = dictDataService.selectMapByType(BizConst.DIETARY_TYPES).getData();
            Map<Long, String> recipeCategoryMap = recipeCategoryMapper.selectList(Wrappers.lambdaQuery(SysRecipeCategory.class)
                    .select(SysRecipeCategory::getRecipeCategoryId, SysRecipeCategory::getName)
                    .in(SysRecipeCategory::getRecipeCategoryId, result.getRecords().stream().map(SysRecipeVo::getCategoryId).collect(Collectors.toList())))
                .stream().collect(Collectors.toMap(k1 -> k1.getRecipeCategoryId(), k2 -> k2.getName(), (k1, k2) -> k1));

            result.getRecords().forEach(v -> {
                SysDictDataVo dataVo = dietaryTypesMap.get(v.getBaseDiet());
                if (ObjUtil.isNotNull(dataVo)) {
                    v.setBaseDietName(dataVo.getDictLabel());
                }
                v.setCategoryName(recipeCategoryMap.get(v.getCategoryId()));
            });
        }
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的食谱管理列表
     *
     * @param bo 查询条件
     * @return 食谱管理列表
     */
    @Override
    public List<SysRecipeVo> queryList(SysRecipeBo bo) {
        LambdaQueryWrapper<SysRecipe> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<SysRecipe> buildQueryWrapper(SysRecipeBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<SysRecipe> lqw = Wrappers.lambdaQuery();
        lqw.orderByAsc(SysRecipe::getRecipeId);
        lqw.like(StringUtils.isNotBlank(bo.getName()), SysRecipe::getName, bo.getName());
        lqw.eq(bo.getCategoryId() != null, SysRecipe::getCategoryId, bo.getCategoryId());
        lqw.eq(StringUtils.isNotBlank(bo.getBaseDiet()), SysRecipe::getBaseDiet, bo.getBaseDiet());
        lqw.eq(StringUtils.isNotBlank(bo.getMealTime()), SysRecipe::getMealTime, bo.getMealTime());
        lqw.eq(bo.getPrice() != null, SysRecipe::getPrice, bo.getPrice());
        lqw.eq(StringUtils.isNotBlank(bo.getSuitableDisease()), SysRecipe::getSuitableDisease, bo.getSuitableDisease());
        lqw.eq(StringUtils.isNotBlank(bo.getAvoidDisease()), SysRecipe::getAvoidDisease, bo.getAvoidDisease());
        lqw.eq(StringUtils.isNotBlank(bo.getSuitableDept()), SysRecipe::getSuitableDept, bo.getSuitableDept());
        lqw.eq(StringUtils.isNotBlank(bo.getSmartRecommend()), SysRecipe::getSmartRecommend, bo.getSmartRecommend());
        lqw.eq(StringUtils.isNotBlank(bo.getCookingMethod()), SysRecipe::getCookingMethod, bo.getCookingMethod());
        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysRecipe::getStatus, bo.getStatus());
        return lqw;
    }

    /**
     * 新增食谱管理
     *
     * @param bo 食谱管理
     * @return 是否新增成功
     */
    @Override
    @Transactional
    public Boolean insertByBo(SysRecipeBo bo) {
        SysRecipe add = MapstructUtils.convert(bo, SysRecipe.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            List<SysRecipeFoodIngredient> fList = bo.getFoodList().stream().map(food -> {
                SysRecipeFoodIngredient foodIngredient = MapstructUtils.convert(food, SysRecipeFoodIngredient.class);
                if (ObjUtil.isNull(foodIngredient.getQuantity())) {
                    throw new ServiceException("食材用量不能为空");
                }
                foodIngredient.setRecipeId(add.getRecipeId());
                return foodIngredient;
            }).collect(Collectors.toList());
            sysRecipeFoodIngredientMapper.insertBatch(fList);
            bo.setRecipeId(add.getRecipeId());
        }
        return flag;
    }

    /**
     * 修改食谱管理
     *
     * @param bo 食谱管理
     * @return 是否修改成功
     */
    @Override
    @Transactional
    public Boolean updateByBo(SysRecipeBo bo) {
        if (ObjUtil.isNull(bo.getRecipeId())) {
            throw new ServiceException("膳食Id不能为空");
        }
        SysRecipe update = MapstructUtils.convert(bo, SysRecipe.class);
        validEntityBeforeSave(update);
        sysRecipeFoodIngredientMapper.delete(Wrappers.lambdaUpdate(SysRecipeFoodIngredient.class)
            .eq(SysRecipeFoodIngredient::getRecipeId, bo.getRecipeId()));

        List<SysRecipeFoodIngredient> fList = bo.getFoodList().stream().map(food -> {
            SysRecipeFoodIngredient foodIngredient = MapstructUtils.convert(food, SysRecipeFoodIngredient.class);
            if (ObjUtil.isNull(foodIngredient.getQuantity())) {
                throw new ServiceException("食材用量不能为空");
            }
            foodIngredient.setRecipeId(bo.getRecipeId());
            return foodIngredient;
        }).collect(Collectors.toList());
        sysRecipeFoodIngredientMapper.insertBatch(fList);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(SysRecipe entity) {
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除食谱管理信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteByIds(ids) > 0;
    }
}
