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

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSONObject;
import com.bonus.canteen.core.common.utils.EasyExcelUtil;
import com.bonus.canteen.core.common.utils.LogUtil;
import com.bonus.canteen.core.cook.domain.*;
import com.bonus.canteen.core.cook.dto.DishesImportCheckResult;
import com.bonus.canteen.core.cook.dto.MenuDishesImportDTO;
import com.bonus.canteen.core.cook.enums.MenuDishesTypeDefaultFlagEnum;
import com.bonus.canteen.core.cook.enums.MenuMaterialTypeEnum;
import com.bonus.canteen.core.cook.enums.MenuSalesTypeEnum;
import com.bonus.canteen.core.cook.mapper.CookNutritionMapper;
import com.bonus.canteen.core.utils.Arith;
import com.bonus.canteen.core.utils.NutritionEntity;
import com.bonus.canteen.core.utils.NutritionEntityUtil;
import com.bonus.common.core.exception.ServiceException;
import com.bonus.common.core.utils.DateUtils;
import com.bonus.common.core.utils.StringUtils;
import com.bonus.common.security.utils.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.stereotype.Service;
import com.bonus.canteen.core.cook.mapper.CookDishesMapper;
import com.bonus.canteen.core.cook.service.ICookDishesService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;

/**
 * 菜品信息Service业务层处理
 *
 * @author xsheng
 * @date 2025-05-25
 */
@Service
public class CookDishesServiceImpl implements ICookDishesService {
    private static final Logger log = LoggerFactory.getLogger(CookDishesServiceImpl.class);

    @Autowired
    private CookDishesMapper cookDishesMapper;
    @Autowired
    private CookNutritionMapper cookNutritionMapper;
    @Lazy
    @Resource(
            name = "smartCanteenTaskExecutor"
    )
    private AsyncTaskExecutor asyncTaskExecutor;

    /**
     * 查询菜品信息
     *
     * @param dishesId 菜品信息主键
     * @return 菜品信息
     */
    @Override
    public CookDishes selectCookDishesByDishesId(Long dishesId) {
        CookDishes cookDishes = cookDishesMapper.selectCookDishesByDishesId(dishesId);
        List<CookDishesMaterial> dishesMaterialList = cookDishesMapper.selectCookDishesMaterialList(cookDishes);
        cookDishes.setDishesMaterialList(dishesMaterialList);
        return cookDishes;
    }

    /**
     * 查询菜品信息列表
     *
     * @param cookDishes 菜品信息
     * @return 菜品信息
     */
    @Override
    public List<CookDishes> selectCookDishesList(CookDishes cookDishes) {
        return cookDishesMapper.selectCookDishesList(cookDishes);
    }

    /**
     * 新增菜品信息
     *
     * @param cookDishes 菜品信息
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertCookDishes(CookDishes cookDishes) {
        cookDishes.setCreateTime(DateUtils.getNowDate());
        try {
            checkParam(cookDishes, false);
            //计算营养信息含量
            //添加菜品
            cookDishesMapper.insertCookDishes(cookDishes);
            //添加菜品-原料关联关系
            if (cookDishes.getDishesMaterialList() != null && !cookDishes.getDishesMaterialList().isEmpty()) {
                cookDishesMapper.insertDishesMaterial(cookDishes.getDishesMaterialList(), cookDishes.getDishesId());
                calculateNutritionalInfo(cookDishes);
            }
            return 1;
        } catch (Exception e) {
            throw new ServiceException("操作失败: " + e.getMessage());
        }
    }


    /**
     * 修改菜品信息
     *
     * @param cookDishes 菜品信息
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateCookDishes(CookDishes cookDishes) {
        cookDishes.setUpdateTime(DateUtils.getNowDate());
        try {
            checkParam(cookDishes, true);
            //修改菜品
            cookDishesMapper.updateCookDishes(cookDishes);
            //删除菜品-原料关联关系
            cookDishesMapper.deleteDishesMaterialByDishesId(cookDishes.getDishesId());
            //添加菜品-原料关联关系
            if (cookDishes.getDishesMaterialList() != null && !cookDishes.getDishesMaterialList().isEmpty()) {
                cookDishesMapper.insertDishesMaterial(cookDishes.getDishesMaterialList(), cookDishes.getDishesId());
                calculateNutritionalInfo(cookDishes);
            }
            return 1;
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
    }

    public void checkParam(CookDishes cookDishes, boolean isUpdate) {
        if (cookDishes.getDishesId() == null && isUpdate) {
            throw new ServiceException("菜品ID不能为空");
        }
//        if (StringUtils.isNull(cookDishes.getAreaId()) || cookDishes.getAreaId() == 0) {
//            throw new ServiceException("区域不能为空");
//        }
//        if (StringUtils.isNull(cookDishes.getCanteenId()) || cookDishes.getCanteenId() == 0) {
//            throw new ServiceException("食堂不能为空");
//        }
//        if (StringUtils.isNull(cookDishes.getStallId()) || cookDishes.getStallId() == 0) {
//            throw new ServiceException("档口不能为空");
//        }
        if (StringUtils.isNull(cookDishes.getTypeId()) || cookDishes.getTypeId() == 0) {
            throw new ServiceException("菜品分类不能为空");
        }
        if (StringUtils.isNull(cookDishes.getPrice()) || cookDishes.getPrice() == 0) {
            throw new ServiceException("菜品价格不能为空");
        }
        if (StringUtils.isNull(cookDishes.getWeight()) || cookDishes.getWeight().compareTo(BigDecimal.ZERO) < 0) {
            throw new ServiceException("菜品份量不能为空或者小于0");
        }
        if (cookDishesMapper.checkDishIsExist(cookDishes) > 0) {
            throw new ServiceException("菜品名称已存在");
        }
    }

    /**
     * 批量删除菜品信息
     *
     * @param dishesIds 需要删除的菜品信息主键
     * @return 结果
     */
    @Override
    public int deleteCookDishesByDishesIds(Long[] dishesIds) {
        //查询所有当天及之后的菜品计划detailId
        List<Long> detailIdList = cookDishesMapper.selectDishPlanDetailIdByDishId(dishesIds, DateUtils.getDate());
        //删除菜品计划cook_recipe_dishes  菜品计划菜品关联表
        if (detailIdList != null && !detailIdList.isEmpty()) {
            cookDishesMapper.deleteCookDishesByDishPlanDetailIds(detailIdList, dishesIds);
        }
        return cookDishesMapper.deleteCookDishesByDishesIds(dishesIds);
    }

    /**
     * 删除菜品信息信息
     *
     * @param dishesId 菜品信息主键
     * @return 结果
     */
    @Override
    public int deleteCookDishesByDishesId(Long dishesId) {
        return cookDishesMapper.deleteCookDishesByDishesId(dishesId);
    }

    private void calculateNutritionalInfo(CookDishes cookDishes) {
        List<CookDishesMaterial> dishesMaterialList = cookDishes.getDishesMaterialList();
        List<NutritionEntity> nutritionEntityList = new ArrayList<>();
        for (CookDishesMaterial i : dishesMaterialList) {
            NutritionEntity nutritionEntity = cookNutritionMapper.getNutritionEntity(i);
            if (nutritionEntity != null) {
                log.debug("原料ID: {}, 初始钙含量: {}", i.getMaterialId(), nutritionEntity.getCalcium());
                nutritionEntity.setWeight(Double.valueOf(i.getWeight() + ""));
                nutritionEntity.setBaseWeight(100.0);
                nutritionEntityList.add(nutritionEntity);
            }
        }
        NutritionEntity nutritionEntity = NutritionEntityUtil.countNutrition(nutritionEntityList);
        if (!nutritionEntityList.isEmpty()) {
            log.debug("总和: {}", nutritionEntity.getCalcium());
            System.err.println("总和：" + nutritionEntity.getCalcium());
            //计算营养成分
            countNum(nutritionEntity, Double.valueOf(cookDishes.getWeight() + ""));
            System.err.println("计算后：" + nutritionEntity.getCalcium());
            log.debug("计算后: {}", nutritionEntity.getCalcium());
            cookDishesMapper.updateFinalNutrition(nutritionEntity, cookDishes.getDishesId());
        }
    }

    private void countNum(NutritionEntity nutritionEntity, Double weight) {
        Field[] fields = nutritionEntity.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Double value = (Double) field.get(nutritionEntity);
                if (value != null) {
                    log.debug("先前值----：{}", value);
                    double proportion = Arith.div(weight, 100);
                    value = Arith.mul(value, proportion);
                    field.set(nutritionEntity, value);
                    log.debug("后来值++++：{}", value);
                } else {
                    field.set(nutritionEntity, 0.00);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public DishesImportCheckResult importDishesCheck(MultipartFile excel) {
        List<MenuDishesImportDTO> menuDishesImportDTOS = null;
        DishesImportCheckResult dishesImportCheckResult = new DishesImportCheckResult();
        try {
            menuDishesImportDTOS = EasyExcelUtil.readSingleExcel(excel, new MenuDishesImportDTO(), 1);
        } catch (Exception var42) {
            throw new ServiceException("导入新增菜品错误" + var42.getMessage());
        }
        if (CollectionUtils.isEmpty(menuDishesImportDTOS)) {
            throw new ServiceException("导入新增菜品为空");
        }
        // 初始化上下文
        List<String> sqlDishesNameList = cookDishesMapper.selectDishesNameAndCanteenName();
        ImportDishesContext context = getImportDishesContext(null);
        Map<String, Long> areaMap = context.getAreaMap();
        Map<String, Long> typeMap = context.getTypeMap();
        Map<String, AllocStall> stallMap = context.getAllocStallMap();
        Map<String, AllocCanteen> canteenMap = context.getAllocCanteenMap();
        Map<Long, AllocArea> areaIdMap = context.getAllocAreaIdMap();
        Map<Long, AllocCanteen> canteenIdMap = context.getAllocCanteenIdMap();
        Map<String, Long> materialMap = context.getMaterialMap();
        List<Integer> salesModeKeys = Arrays.stream(MenuSalesTypeEnum.values()).map(MenuSalesTypeEnum::key).collect(Collectors.toList());
        List<String> materialTypeKeys = Arrays.stream(MenuMaterialTypeEnum.values()).map(MenuMaterialTypeEnum::key).map(Object::toString).collect(Collectors.toList());

        List<String> dishesNameList = new ArrayList<>();
        List<MenuDishesImportDTO> successList = new ArrayList<>();
        List<MenuDishesImportDTO> errorList = new ArrayList<>();

        for (MenuDishesImportDTO data : menuDishesImportDTOS) {
            try {
                // 校验数据
                if (!validateData(data, sqlDishesNameList, dishesNameList, context, salesModeKeys, materialTypeKeys)) {
                    errorList.add(data);
                    continue;
                }

                // 校验区域、食堂、档口
                if (!validateLocation(data, areaMap, stallMap, canteenMap, areaIdMap, canteenIdMap)) {
                    errorList.add(data);
                    continue;
                }

                // 校验通过
                dishesNameList.add(generateUniqueName(data));
                successList.add(data);
            } catch (Exception e) {
                data.setFailReason("未知错误: " + e.getMessage());
                errorList.add(data);
            }
        }
        dishesImportCheckResult.setErrorList(errorList);
        dishesImportCheckResult.setSuccessList(successList);
        return dishesImportCheckResult;
    }


    private boolean validateData(MenuDishesImportDTO data, List<String> sqlDishesNameList, List<String> dishesNameList,
                                 ImportDishesContext context, List<Integer> salesModeKeys, List<String> materialTypeKeys) {
        // 校验价格
        if (data.getPrice().compareTo(BigDecimal.ZERO) < 0) {
            data.setFailReason("菜品价格为负数");
            return false;
        }

        // 校验计价方式
        if (!salesModeKeys.contains(data.getSalesMode())) {
            data.setFailReason("计价方式只能填写1或者2");
            return false;
        }


        // 校验菜品类别
        if (CharSequenceUtil.isNotBlank(data.getDishesTypeName()) && context.getTypeMap().get(data.getDishesTypeName()) == null) {
            data.setFailReason(data.getDishesTypeName() + "菜品类别不存在");
            return false;
        }

        if (CharSequenceUtil.isBlank(data.getDishesTypeName())){
            data.setTypeId("0");
        }else{
            data.setTypeId(String.valueOf(context.getTypeMap().get(data.getDishesTypeName())));
        }

        // 校验菜品名称
        String uniqueName = generateUniqueName(data);
        if (CharSequenceUtil.isBlank(data.getDishesName())) {
            data.setFailReason("菜品名称为空");
            return false;
        }
        if (dishesNameList.contains(uniqueName) || sqlDishesNameList.contains(uniqueName)) {
            data.setFailReason("菜品名称重复或已存在");
            return false;
        }
        int two = 2;
        int one = 1;
        if(data.getSalesMode() == one && data.getWeight() == null ){
            data.setFailReason("按份菜品必须填写标准份量");
            return false;
        }

        // 校验规格称重
        if (data.getSalesMode() == two && data.getSpecificationOfWeighing() != null &&
                data.getSpecificationOfWeighing().compareTo(BigDecimal.valueOf(50)) != 0 &&
                data.getSpecificationOfWeighing().compareTo(BigDecimal.valueOf(100)) != 0) {
            data.setFailReason("称重菜品必须填写规格称重");
            return false;
        }

        // 校验原料
        if (!validateMaterials(data, context.getMaterialMap(), materialTypeKeys)) {
            return false;
        }

        return true;
    }


    private boolean validateMaterials(MenuDishesImportDTO data, Map<String, Long> materialMap, List<String> materialTypeKeys) {
        if (CharSequenceUtil.isNotBlank(data.getMaterialNames())) {
            String[] materials = data.getMaterialNames().replace("；", ";").split(";");
//            BigDecimal totalWeight = BigDecimal.ZERO;
            for (String material : materials) {
                String[] parts = material.split("&");
                if (parts.length != 3 || materialMap.get(parts[0]) == null || !materialTypeKeys.contains(parts[1]) || !NumberUtil.isNumber(parts[2])) {
                    data.setFailReason("原料格式或数据错误: " + material);
                    return false;
                }
                if (new BigDecimal(parts[2]).compareTo(BigDecimal.ZERO) <= 0) {
                    data.setFailReason("原料重量为负数: " + parts[2]);
                    return false;
                }
//                totalWeight = totalWeight.add(new BigDecimal(parts[2]));
            }
//            if (totalWeight.compareTo(data.getWeight()) < 0) {
//                data.setFailReason("原料总重量小于标准重量，不符合要求");
//                return false;
//            }
        }
        return true;
    }

    private boolean validateLocation(MenuDishesImportDTO data, Map<String, Long> areaMap, Map<String, AllocStall> stallMap,
                                     Map<String, AllocCanteen> canteenMap, Map<Long, AllocArea> areaIdMap, Map<Long, AllocCanteen> canteenIdMap) {
        // 校验档口
        if (ObjectUtil.isNotEmpty(data.getStallName())) {
            AllocStall stall = stallMap.get(data.getStallName());
            if (stall == null || ObjectUtil.isEmpty(data.getCanteenName()) ||
                    !canteenIdMap.get(stall.getCanteenId()).getCanteenName().equals(data.getCanteenName())) {
                data.setFailReason("档口或食堂名称错误");
                return false;
            }
            data.setStallId(String.valueOf(stall.getStallId()));
        }

        // 校验食堂和区域
        if (ObjectUtil.isNotEmpty(data.getCanteenName())) {
            AllocCanteen canteen = canteenMap.get(data.getCanteenName());
            if (canteen == null || ObjectUtil.isEmpty(data.getAreaName()) ||
                    !areaIdMap.get(canteen.getAreaId()).getAreaName().equals(data.getAreaName())) {
                data.setFailReason("食堂或区域名称错误");
                return false;
            }
            data.setCanteenId(String.valueOf(canteen.getCanteenId()));
            data.setAreaId(String.valueOf(canteen.getAreaId()));
        }

        // 校验区域
        if (ObjectUtil.isNotEmpty(data.getAreaName()) && !areaMap.containsKey(data.getAreaName())) {
            data.setFailReason("区域名称不存在");
            return false;
        }

        return true;
    }

    private String generateUniqueName(MenuDishesImportDTO data) {
        return Stream.of(data.getDishesName(), data.getAreaName(), data.getCanteenName(), data.getStallName())
                .map(x -> CharSequenceUtil.isBlank(x) ? "*" : x)
                .collect(Collectors.joining("-"));
    }

    public ImportDishesContext getImportDishesContext(String username) {
        ImportDishesContext importDishesContext = new ImportDishesContext();
        List<MapBean> materialMapList = cookDishesMapper.getMenuMaterialMap();
        Map<String, Long> materialMap = new HashMap<>(100);
        materialMapList.forEach(map -> materialMap.put(map.getKey(), toLong(map.getValue())));

        List<AllocArea> allocAreaList = cookDishesMapper.selectAreaList();
        Map<String, AllocArea> areaMap = new HashMap<>(100);
        allocAreaList.forEach(allocArea -> {
            String areaName = allocArea.getAreaName();
            if (areaName != null) {
                areaMap.put(areaName, allocArea);
            }
        });

        List<AllocCanteen> allocCanteenList = cookDishesMapper.selectCanteenList();
        Map<String, AllocCanteen> canteenMap = new HashMap<>(100);
        allocCanteenList.forEach(allocCanteen -> {
            String canteenName = allocCanteen.getCanteenName();
            if (canteenName != null) {
                canteenMap.put(canteenName, allocCanteen);
            }
        });

        List<AllocStall> allocStallList = cookDishesMapper.selectStallList();
        Map<String, AllocStall> stallMap = new HashMap<>(100);
        allocStallList.forEach(allocStall -> {
            String stallName = allocStall.getStallName();
            if (stallName != null) {
                stallMap.put(stallName, allocStall);
            }
        });

        List<MapBean> typeMapList = this.cookDishesMapper.getCookMaterialTypeMap();
        Map<String, Long> typeMap = new HashMap<>(100);
        typeMapList.forEach(map -> typeMap.put(map.getKey(), toLong(map.getValue())));


        importDishesContext.setAllocAreaMap(areaMap);
        importDishesContext.setAllocStallMap(stallMap);
        importDishesContext.setAllocCanteenMap(canteenMap);
        importDishesContext.setTypeMap(typeMap);
        importDishesContext.setMaterialMap(materialMap);
        importDishesContext.setUsername(username);
        return importDishesContext;
    }

    private Long toLong(Object value) {
        if (value == null) {
            return 0L;
        }
        if (value instanceof Number) {
            return ((Number) value).longValue();
        }
        try {
            return Long.parseLong(value.toString());
        } catch (NumberFormatException e) {
            return 0L; // 或抛出异常
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean newImportDishes(List<MenuDishesImportDTO> menuDishesImportDTO) {
        try {
            String currentTraceId = LogUtil.getCurrentTraceId();
            String username = SecurityUtils.getUsername();
            this.asyncTaskExecutor.execute(() -> {
                LogUtil.putLogTraceId(currentTraceId);
                this.importDishesDTO(menuDishesImportDTO, username);
            });
        } catch (Exception var7) {
            throw new ServiceException("批量导入菜品发生错误");
        }
        return true;
    }

    private void importDishesDTO(List<MenuDishesImportDTO> menuDishesImportList, String username) {
//        try {
        // 重新组合数据
        List<CookDishes> dishesList = new ArrayList<>();
        ImportDishesContext importDishesContext = this.getImportDishesContext(null);
        Map<String, Long> materialMap = importDishesContext.getMaterialMap();
        for (MenuDishesImportDTO menuDishesImportDTO : menuDishesImportList) {
            CookDishes dishes = new CookDishes();
            dishes.setCanteenId(menuDishesImportDTO.getCanteenId() == null ? null : Long.valueOf(menuDishesImportDTO.getCanteenId()));
            dishes.setStallId(menuDishesImportDTO.getStallId() == null ? null : Long.valueOf(menuDishesImportDTO.getStallId()));
            dishes.setAreaId(menuDishesImportDTO.getAreaId() == null ? null : Long.valueOf(menuDishesImportDTO.getAreaId()));
            dishes.setDishesName(menuDishesImportDTO.getDishesName());
            dishes.setPrice(menuDishesImportDTO.getPrice() == null ? null : Long.valueOf(String.valueOf(menuDishesImportDTO.getPrice())));
            dishes.setWeight(menuDishesImportDTO.getWeight() == null ? null : menuDishesImportDTO.getWeight());
            dishes.setWeightDeviation(BigDecimal.ZERO);
            dishes.setTypeId(menuDishesImportDTO.getTypeId() == null ? null : Long.valueOf(menuDishesImportDTO.getTypeId()));
            dishes.setPungencyDegree(0L);
            dishes.setRecommend("0");
            dishes.setSalesMode(menuDishesImportDTO.getSalesMode() == null ? null : Long.valueOf(menuDishesImportDTO.getSalesMode()));
            dishes.setUnitPrice(menuDishesImportDTO.getSpecificationOfWeighing() == null ? null : Long.valueOf(String.valueOf(menuDishesImportDTO.getSpecificationOfWeighing())));
            String[] materialList = menuDishesImportDTO.getMaterialNames().replace("；", ";").split(";");
            List<CookDishesMaterial> dishesMaterialList = new ArrayList<>();
            for (String material : materialList) {
                String[] materialSplit = material.split("&");
                if (materialSplit.length != 3) {
                    continue; // 跳过格式不正确的原料
                }
                CookDishesMaterial dishesMaterial = new CookDishesMaterial();
                try {
                    dishesMaterial.setWeight(BigDecimal.valueOf(Double.parseDouble(materialSplit[2])));
                } catch (NumberFormatException e) {
                    dishesMaterial.setWeight(BigDecimal.ZERO);
                }
                dishesMaterial.setMaterialName(materialSplit[0]);
                try {
                    dishesMaterial.setMaterialType(Long.valueOf(materialSplit[1]));
                } catch (NumberFormatException e) {
                    dishesMaterial.setMaterialType(0L);
                }
                dishesMaterial.setMaterialId(materialMap.getOrDefault(materialSplit[0], 0L));
                dishesMaterialList.add(dishesMaterial);
            }
            dishes.setDishesMaterialList(dishesMaterialList);
            dishesList.add(dishes);
        }
        dishesList.forEach(this::insertCookDishes);
    }

}
