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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.jeesharp.business.modules.common.support.QueryInCsvSupport;
import com.jeesharp.business.modules.food.convert.FoodMealRuleConvert;
import com.jeesharp.business.modules.food.dto.FoodMealRuleSaveDTO;
import com.jeesharp.business.modules.food.entity.FoodMealRule;
import com.jeesharp.business.modules.food.entity.FoodMealServiceArea;
import com.jeesharp.business.modules.food.service.FoodMealRuleService;
import com.jeesharp.business.modules.food.service.FoodMealServiceAreaService;
import com.jeesharp.business.modules.food.vo.FoodMealRuleInfoVO;
import com.jeesharp.business.modules.food.vo.FoodMealRuleListVO;
import com.jeesharp.business.modules.food.vo.FoodMealRuleQueryVO;
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.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

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

    private static final Set<String> MEAL_TYPES = Set.of("breakfast", "lunch", "dinner", "snack");
    private static final Set<String> SCOPE_TYPES = Set.of("all", "personal", "company");
    private static final Set<String> CANCEL_RULE_TYPES = Set.of("none", "before_deadline", "anytime");

    private final FoodMealRuleService foodMealRuleService;
    private final FoodMealServiceAreaService foodMealServiceAreaService;

    public FoodMealRuleInfoVO get(String id) {
        FoodMealRule entity = foodMealRuleService.get(id);
        if (entity == null) {
            return null;
        }
        FoodMealRuleInfoVO vo = FoodMealRuleConvert.INSTANCE.toInfoVO(entity);
        enrichInfoDisplay(vo);
        return vo;
    }

    public PageRespVO<FoodMealRuleListVO> findPage(Page<FoodMealRule> page, FoodMealRuleQueryVO query) {
        FoodMealRuleQueryVO prepared = prepareQuery(query);
        if (prepared == null) {
            return emptyPage(page);
        }
        Page<FoodMealRule> result = foodMealRuleService.findPage(page, prepared);
        PageRespVO<FoodMealRuleListVO> pageVo = FoodMealRuleConvert.INSTANCE.toPageRespVO(result);
        enrichListDisplay(pageVo.getRecords());
        return pageVo;
    }

    @Transactional(readOnly = false)
    public void save(FoodMealRuleSaveDTO dto) {
        validateSave(dto);
        FoodMealRule entity = FoodMealRuleConvert.INSTANCE.toEntity(dto);
        if (StrUtil.isBlank(dto.getId())) {
            foodMealRuleService.createRule(entity);
            return;
        }
        foodMealRuleService.updateRule(entity);
    }

    @Transactional(readOnly = false)
    public void delete(String ids) {
        foodMealRuleService.batchDelete(StrUtil.splitTrim(ids, ','));
    }

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

    private void validateSave(FoodMealRuleSaveDTO dto) {
        fillServiceAreaSnapshot(dto);
        if (StrUtil.isNotBlank(dto.getMealType()) && !MEAL_TYPES.contains(dto.getMealType())) {
            throw new BizErrorException("餐别不正确");
        }
        if (!SCOPE_TYPES.contains(dto.getScopeType())) {
            throw new BizErrorException("适用范围不正确");
        }
        if (dto.getAdvanceHours() == null || dto.getAdvanceHours() < 0 || dto.getAdvanceHours() > 720) {
            throw new BizErrorException("提前小时数必须在0到720之间");
        }
        if (dto.getAdvanceDays() == null || dto.getAdvanceDays() < 0 || dto.getAdvanceDays() > 365) {
            throw new BizErrorException("提前天数必须在0到365之间");
        }
        if (dto.getBookableFutureDays() == null || dto.getBookableFutureDays() < 1 || dto.getBookableFutureDays() > 365) {
            throw new BizErrorException("可预订未来天数必须在1到365之间");
        }
        if (!CANCEL_RULE_TYPES.contains(dto.getCancelRuleType())) {
            throw new BizErrorException("取消规则不正确");
        }
        if (dto.getCancelDeadlineHours() != null && dto.getCancelDeadlineHours() < 0) {
            throw new BizErrorException("取消提前小时数不能小于0");
        }
        if (foodMealRuleService.existsSameRule(dto.getId(), dto.getServiceAreaId(), dto.getMealType(), dto.getScopeType())) {
            throw new BizErrorException("同一服务区域、餐别、适用范围的订餐规则已存在");
        }
        validateStatus(dto.getStatus());
    }

    private void fillServiceAreaSnapshot(FoodMealRuleSaveDTO dto) {
        if (StrUtil.isBlank(dto.getServiceAreaId())) {
            dto.setServiceAreaName(null);
            dto.setRestaurantId(null);
            return;
        }
        FoodMealServiceArea area = foodMealServiceAreaService.get(dto.getServiceAreaId());
        if (area == null) {
            throw new BizErrorException("服务区域不存在");
        }
        dto.setServiceAreaName(area.getAreaName());
        dto.setRestaurantId(dto.getServiceAreaId());
    }

    private void validateStatus(Integer status) {
        if (status == null || (status != 0 && status != 1)) {
            throw new BizErrorException("状态值不正确");
        }
    }

    private FoodMealRuleQueryVO prepareQuery(FoodMealRuleQueryVO query) {
        FoodMealRuleQueryVO q = query != null ? query : new FoodMealRuleQueryVO();
        if (StrUtil.isBlank(q.getServiceAreaName()) && StrUtil.isNotBlank(q.getRestaurantName())) {
            q.setServiceAreaName(q.getRestaurantName());
        }
        if (StrUtil.isBlank(q.getServiceAreaId()) && StrUtil.isNotBlank(q.getRestaurantId())) {
            q.setServiceAreaId(q.getRestaurantId());
        }
        if (StrUtil.isNotBlank(q.getServiceAreaName())) {
            List<String> serviceAreaIds = foodMealServiceAreaService.findIdsByNameLike(q.getServiceAreaName().trim());
            q.setServiceAreaName(null);
            q.setRestaurantName(null);
            if (CollUtil.isEmpty(serviceAreaIds)) {
                return null;
            }
            q.setServiceAreaIdInCsv(QueryInCsvSupport.toIdInCsv(serviceAreaIds));
        }
        return q;
    }

    private void enrichListDisplay(List<FoodMealRuleListVO> rows) {
        if (CollUtil.isEmpty(rows)) {
            return;
        }
        List<String> serviceAreaIds = rows.stream()
                .map(FoodMealRuleListVO::getServiceAreaId)
                .filter(StrUtil::isNotBlank)
                .distinct()
                .toList();
        Map<String, String> serviceAreaNameMap = foodMealServiceAreaService.mapAreaNameByIds(serviceAreaIds);
        for (FoodMealRuleListVO row : rows) {
            if (StrUtil.isBlank(row.getServiceAreaId())) {
                row.setServiceAreaName("全局规则");
            } else if (StrUtil.isBlank(row.getServiceAreaName())) {
                row.setServiceAreaName(serviceAreaNameMap.get(row.getServiceAreaId()));
            }
            row.setRestaurantId(row.getServiceAreaId());
            row.setRestaurantName(row.getServiceAreaName());
        }
    }

    private void enrichInfoDisplay(FoodMealRuleInfoVO vo) {
        if (vo == null) {
            return;
        }
        if (StrUtil.isBlank(vo.getServiceAreaId())) {
            vo.setServiceAreaName("全局规则");
        } else if (StrUtil.isBlank(vo.getServiceAreaName())) {
            Map<String, String> nameMap = foodMealServiceAreaService.mapAreaNameByIds(List.of(vo.getServiceAreaId()));
            vo.setServiceAreaName(nameMap.get(vo.getServiceAreaId()));
        }
        vo.setRestaurantId(vo.getServiceAreaId());
        vo.setRestaurantName(vo.getServiceAreaName());
    }

    private static PageRespVO<FoodMealRuleListVO> emptyPage(Page<FoodMealRule> page) {
        PageRespVO<FoodMealRuleListVO> empty = new PageRespVO<>();
        empty.setRecords(Collections.emptyList());
        empty.setTotal(0L);
        empty.setPageNo(page != null ? page.getPageNo() : 1L);
        empty.setPageSize(page != null ? page.getPageSize() : 10L);
        return empty;
    }
}
