package com.jeesharp.business.modules.food.application;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.jeesharp.business.modules.food.convert.FoodMealMenuTemplateConvert;
import com.jeesharp.business.modules.food.dto.FoodMealMenuTemplateItemSaveDTO;
import com.jeesharp.business.modules.food.dto.FoodMealMenuTemplateItemSpecSaveDTO;
import com.jeesharp.business.modules.food.dto.FoodMealMenuTemplateSaveDTO;
import com.jeesharp.business.modules.food.entity.FoodDish;
import com.jeesharp.business.modules.food.entity.FoodMealMenuTemplate;
import com.jeesharp.business.modules.food.entity.FoodMealMenuTemplateItem;
import com.jeesharp.business.modules.food.entity.FoodMealMenuTemplateItemSpec;
import com.jeesharp.business.modules.food.entity.FoodPackage;
import com.jeesharp.business.modules.food.service.FoodDishService;
import com.jeesharp.business.modules.food.service.FoodMealMenuTemplateItemService;
import com.jeesharp.business.modules.food.service.FoodMealMenuTemplateItemSpecService;
import com.jeesharp.business.modules.food.service.FoodMealMenuTemplateService;
import com.jeesharp.business.modules.food.service.FoodPackageService;
import com.jeesharp.business.modules.food.vo.FoodMealMenuTemplateInfoVO;
import com.jeesharp.business.modules.food.vo.FoodMealMenuTemplateItemListVO;
import com.jeesharp.business.modules.food.vo.FoodMealMenuTemplateItemSpecVO;
import com.jeesharp.business.modules.food.vo.FoodMealMenuTemplateListVO;
import com.jeesharp.business.modules.food.vo.FoodMealMenuTemplateQueryVO;
import com.jeesharp.framework.exception.BizErrorException;
import com.jeesharp.framework.pojo.Page;
import com.jeesharp.framework.pojo.query.PageRespVO;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
@Transactional(readOnly = true)
public class FoodMealMenuTemplateApplicationService {

    private static final Set<String> MEAL_TYPES = Set.of("breakfast", "lunch", "dinner", "snack");
    private static final Set<String> SCOPE_TYPES = Set.of("public", "company", "all");

    private final FoodMealMenuTemplateService foodMealMenuTemplateService;
    private final FoodMealMenuTemplateItemService foodMealMenuTemplateItemService;
    private final FoodMealMenuTemplateItemSpecService foodMealMenuTemplateItemSpecService;
    private final FoodDishService foodDishService;
    private final FoodPackageService foodPackageService;

    public FoodMealMenuTemplateInfoVO get(String id) {
        FoodMealMenuTemplate entity = foodMealMenuTemplateService.get(id);
        if (entity == null) {
            return null;
        }
        FoodMealMenuTemplateInfoVO vo = FoodMealMenuTemplateConvert.INSTANCE.toInfoVO(entity);
        vo.setItems(toItemVOList(foodMealMenuTemplateItemService.listByTemplateId(id)));
        return vo;
    }

    public PageRespVO<FoodMealMenuTemplateListVO> findPage(Page<FoodMealMenuTemplate> page, FoodMealMenuTemplateQueryVO query) {
        Page<FoodMealMenuTemplate> result = foodMealMenuTemplateService.findPage(page, query);
        PageRespVO<FoodMealMenuTemplateListVO> vo = FoodMealMenuTemplateConvert.INSTANCE.toPageRespVO(result);
        if (CollUtil.isNotEmpty(vo.getRecords())) {
            for (FoodMealMenuTemplateListVO record : vo.getRecords()) {
                List<FoodMealMenuTemplateItem> items = foodMealMenuTemplateItemService.listByTemplateId(record.getId());
                record.setItemCount(items.size());
                record.setItems(toItemVOList(items));
            }
        }
        return vo;
    }

    @Transactional(readOnly = false)
    public void save(FoodMealMenuTemplateSaveDTO dto) {
        validate(dto);
        FoodMealMenuTemplate entity = FoodMealMenuTemplateConvert.INSTANCE.toEntity(dto);
        if (StrUtil.isBlank(dto.getId())) {
            foodMealMenuTemplateService.createTemplate(entity);
        } else {
            foodMealMenuTemplateService.updateTemplate(entity);
        }
        saveItems(entity, dto.getItems());
    }

    @Transactional(readOnly = false)
    public void updateStatus(String id, Integer status) {
        foodMealMenuTemplateService.updateStatus(id, status);
    }

    @Transactional(readOnly = false)
    public void delete(String ids) {
        for (String id : StrUtil.splitTrim(ids, ',')) {
            foodMealMenuTemplateItemSpecService.deleteByTemplateId(id);
            foodMealMenuTemplateItemService.deleteByTemplateId(id);
        }
        foodMealMenuTemplateService.batchDelete(StrUtil.splitTrim(ids, ','));
    }

    private void validate(FoodMealMenuTemplateSaveDTO dto) {
        if (StrUtil.isBlank(dto.getTemplateName())) {
            throw new BizErrorException("模板名称不能为空");
        }
        if (StrUtil.isNotBlank(dto.getMealType()) && !MEAL_TYPES.contains(dto.getMealType())) {
            throw new BizErrorException("默认餐别不正确");
        }
        if (StrUtil.isNotBlank(dto.getScopeType()) && !SCOPE_TYPES.contains(dto.getScopeType())) {
            throw new BizErrorException("默认适用范围不正确");
        }
        if (CollUtil.isEmpty(dto.getItems())) {
            throw new BizErrorException("请至少配置一个菜单明细");
        }
        for (FoodMealMenuTemplateItemSaveDTO item : dto.getItems()) {
            validateItem(item);
        }
    }

    private void validateItem(FoodMealMenuTemplateItemSaveDTO item) {
        if (item == null || !("dish".equals(item.getItemType()) || "package".equals(item.getItemType()))) {
            throw new BizErrorException("菜单明细类型不正确");
        }
        if ("dish".equals(item.getItemType())) {
            if (StrUtil.isBlank(item.getDishId()) || foodDishService.get(item.getDishId()) == null) {
                throw new BizErrorException("菜品不存在");
            }
        } else if (StrUtil.isBlank(item.getPackageId()) || foodPackageService.get(item.getPackageId()) == null) {
            throw new BizErrorException("套餐不存在");
        }
        if (item.getSalePrice() == null || item.getSalePrice().compareTo(BigDecimal.ZERO) < 0) {
            throw new BizErrorException("菜单明细售价不能小于0");
        }
        if (item.getStockQuantity() != null && item.getStockQuantity() < 0) {
            throw new BizErrorException("默认库存不能小于0");
        }
        if (item.getLimitQuantityPerUser() != null && item.getLimitQuantityPerUser() < 1) {
            throw new BizErrorException("限购数量必须大于0");
        }
        if (CollUtil.isNotEmpty(item.getSpecs())) {
            for (FoodMealMenuTemplateItemSpecSaveDTO spec : item.getSpecs()) {
                if (spec == null || StrUtil.isBlank(spec.getSpecName())) {
                    throw new BizErrorException("规格名称不能为空");
                }
                if (spec.getSalePrice() == null || spec.getSalePrice().compareTo(BigDecimal.ZERO) < 0) {
                    throw new BizErrorException("规格价格不能小于0");
                }
            }
        }
    }

    private void saveItems(FoodMealMenuTemplate template, List<FoodMealMenuTemplateItemSaveDTO> items) {
        foodMealMenuTemplateItemSpecService.deleteByTemplateId(template.getId());
        foodMealMenuTemplateItemService.deleteByTemplateId(template.getId());
        int index = 0;
        for (FoodMealMenuTemplateItemSaveDTO dto : items) {
            FoodDish dish = sourceDish(dto);
            FoodPackage pkg = sourcePackage(dto);
            FoodMealMenuTemplateItem item = new FoodMealMenuTemplateItem();
            item.setTemplateId(template.getId());
            item.setItemType(dto.getItemType());
            item.setDishId("dish".equals(dto.getItemType()) ? dto.getDishId() : null);
            item.setPackageId("package".equals(dto.getItemType()) ? dto.getPackageId() : null);
            item.setItemName(sourceName(dto, dish, pkg));
            item.setItemImage(sourceImage(dto, dish, pkg));
            item.setImagesJson(sourceImages(dto, dish, pkg));
            item.setTagsJson(sourceTags(dto, dish));
            item.setDescriptionHtml(sourceDescription(dto, dish));
            item.setSalePrice(dto.getSalePrice());
            item.setStockQuantity(dto.getStockQuantity());
            item.setLimitQuantityPerUser(dto.getLimitQuantityPerUser());
            item.setSortNo(dto.getSortNo() == null ? index : dto.getSortNo());
            foodMealMenuTemplateItemService.save(item);
            saveSpecs(template, item, dto.getSpecs());
            index++;
        }
    }

    private void saveSpecs(FoodMealMenuTemplate template, FoodMealMenuTemplateItem item, List<FoodMealMenuTemplateItemSpecSaveDTO> specs) {
        if (CollUtil.isEmpty(specs)) {
            return;
        }
        int index = 0;
        for (FoodMealMenuTemplateItemSpecSaveDTO dto : specs) {
            FoodMealMenuTemplateItemSpec spec = new FoodMealMenuTemplateItemSpec();
            spec.setTemplateId(template.getId());
            spec.setTemplateItemId(item.getId());
            spec.setSpecName(StrUtil.trim(dto.getSpecName()));
            spec.setSpecDescription(StrUtil.trimToNull(dto.getSpecDescription()));
            spec.setSalePrice(dto.getSalePrice());
            spec.setStockQuantity(dto.getStockQuantity());
            spec.setLimitQuantityPerUser(dto.getLimitQuantityPerUser());
            spec.setSortNo(dto.getSortNo() == null ? index : dto.getSortNo());
            foodMealMenuTemplateItemSpecService.save(spec);
            index++;
        }
    }

    private FoodDish sourceDish(FoodMealMenuTemplateItemSaveDTO dto) {
        return "dish".equals(dto.getItemType()) ? foodDishService.get(dto.getDishId()) : null;
    }

    private FoodPackage sourcePackage(FoodMealMenuTemplateItemSaveDTO dto) {
        return "package".equals(dto.getItemType()) ? foodPackageService.get(dto.getPackageId()) : null;
    }

    private String sourceName(FoodMealMenuTemplateItemSaveDTO dto, FoodDish dish, FoodPackage pkg) {
        if ("dish".equals(dto.getItemType())) {
            return dish == null ? null : dish.getName();
        }
        return pkg == null ? null : pkg.getName();
    }

    private String sourceImage(FoodMealMenuTemplateItemSaveDTO dto, FoodDish dish, FoodPackage pkg) {
        if ("dish".equals(dto.getItemType())) {
            return dish == null ? null : dish.getImageUrl();
        }
        return pkg == null ? null : firstImage(pkg.getImagesJson());
    }

    private String sourceImages(FoodMealMenuTemplateItemSaveDTO dto, FoodDish dish, FoodPackage pkg) {
        if ("dish".equals(dto.getItemType())) {
            return normalizeArrayJson(dish == null ? null : dish.getImagesJson());
        }
        return normalizeArrayJson(pkg == null ? null : pkg.getImagesJson());
    }

    private String sourceTags(FoodMealMenuTemplateItemSaveDTO dto, FoodDish dish) {
        if ("dish".equals(dto.getItemType())) {
            return normalizeArrayJson(dish == null ? null : dish.getTagsJson());
        }
        return "[]";
    }

    private String sourceDescription(FoodMealMenuTemplateItemSaveDTO dto, FoodDish dish) {
        if ("dish".equals(dto.getItemType())) {
            return dish == null ? null : dish.getDescriptionHtml();
        }
        return null;
    }

    private String normalizeArrayJson(String value) {
        if (StrUtil.isBlank(value)) {
            return "[]";
        }
        try {
            return JSONUtil.parseArray(value).toString();
        } catch (Exception e) {
            return "[]";
        }
    }

    private String firstImage(String imagesJson) {
        if (StrUtil.isBlank(imagesJson)) {
            return null;
        }
        try {
            return JSONUtil.parseArray(imagesJson).stream()
                .map(value -> value == null ? null : String.valueOf(value).trim())
                .filter(StrUtil::isNotBlank)
                .findFirst()
                .orElse(null);
        } catch (Exception e) {
            return null;
        }
    }

    private List<FoodMealMenuTemplateItemListVO> toItemVOList(List<FoodMealMenuTemplateItem> items) {
        if (CollUtil.isEmpty(items)) {
            return List.of();
        }
        Map<String, List<FoodMealMenuTemplateItemSpec>> specMap = foodMealMenuTemplateItemSpecService
            .listByTemplateItemIds(items.stream().map(FoodMealMenuTemplateItem::getId).toList())
            .stream().collect(Collectors.groupingBy(FoodMealMenuTemplateItemSpec::getTemplateItemId));
        return items.stream().map(item -> {
            FoodMealMenuTemplateItemListVO vo = new FoodMealMenuTemplateItemListVO();
            vo.setId(item.getId());
            vo.setTemplateId(item.getTemplateId());
            vo.setItemType(item.getItemType());
            vo.setDishId(item.getDishId());
            vo.setPackageId(item.getPackageId());
            vo.setItemName(item.getItemName());
            vo.setItemImage(item.getItemImage());
            vo.setImagesJson(item.getImagesJson());
            vo.setTagsJson(item.getTagsJson());
            vo.setDescriptionHtml(item.getDescriptionHtml());
            vo.setSalePrice(item.getSalePrice());
            vo.setStockQuantity(item.getStockQuantity());
            vo.setLimitQuantityPerUser(item.getLimitQuantityPerUser());
            vo.setSortNo(item.getSortNo());
            vo.setSpecs(specMap.getOrDefault(item.getId(), List.of()).stream().map(this::toSpecVO).toList());
            return vo;
        }).toList();
    }

    private FoodMealMenuTemplateItemSpecVO toSpecVO(FoodMealMenuTemplateItemSpec spec) {
        FoodMealMenuTemplateItemSpecVO vo = new FoodMealMenuTemplateItemSpecVO();
        vo.setId(spec.getId());
        vo.setTemplateId(spec.getTemplateId());
        vo.setTemplateItemId(spec.getTemplateItemId());
        vo.setSpecName(spec.getSpecName());
        vo.setSpecDescription(spec.getSpecDescription());
        vo.setSalePrice(spec.getSalePrice());
        vo.setStockQuantity(spec.getStockQuantity());
        vo.setLimitQuantityPerUser(spec.getLimitQuantityPerUser());
        vo.setSortNo(spec.getSortNo());
        return vo;
    }

}
