package cn.iocoder.yudao.module.system.service.restaurant;

import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.ImportResultVO;
import cn.iocoder.yudao.module.system.controller.admin.restaurant.vo.CateringPlanPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.restaurant.vo.CateringPlanRespVO;
import cn.iocoder.yudao.module.system.controller.admin.restaurant.vo.CateringPlanSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.restaurant.CateringPlanDO;
import cn.iocoder.yudao.module.system.dal.dataobject.restaurant.CateringPlanItemDO;
import cn.iocoder.yudao.module.system.dal.dataobject.restaurant.DishesDO;
import cn.iocoder.yudao.module.system.dal.dataobject.restaurant.RestaurantManagementDO;
import cn.iocoder.yudao.module.system.dal.mysql.restaurant.CateringPlanItemMapper;
import cn.iocoder.yudao.module.system.dal.mysql.restaurant.CateringPlanMapper;
import cn.iocoder.yudao.module.system.dal.mysql.restaurant.DishesMapper;
import cn.iocoder.yudao.module.system.dal.mysql.restaurant.RestaurantManagementMapper;
import cn.iocoder.yudao.module.system.util.ImportUtil;
import cn.iocoder.yudao.module.system.util.http.HttpClientUtil;
import cn.iocoder.yudao.module.system.util.http.HttpClientResult;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 餐谱计划 Service 实现类
 *
 * @author 系统管理员
 */
@Slf4j
@Service
@Validated
public class CateringPlanServiceImpl implements CateringPlanService {
    @Resource
    private DishesMapper dishesMapper;
    @Resource
    private CateringPlanMapper cateringPlanMapper;
    @Resource
    private CateringPlanItemMapper cateringPlanItemMapper;

    @Resource
    private RestaurantManagementMapper restaurantManagementMapper;

    @Override
    public Long createCateringPlan(CateringPlanSaveReqVO createReqVO) {
        if (createReqVO.getId() != null) {
            createReqVO.setId(null);
        }
        CateringPlanDO cateringPlan = new CateringPlanDO();
        BeanUtils.copyProperties(createReqVO, cateringPlan);
        cateringPlan.setCreatedTime(new Date());
        cateringPlan.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
        cateringPlanMapper.insert(cateringPlan);

        List<CateringPlanItemDO> items = createReqVO.getItems();
        for (CateringPlanItemDO item : items) {
            item.setCateringPlanId(cateringPlan.getId());
            cateringPlanItemMapper.insert(item);
        }
        invokeWechatInterface(createReqVO.getRestaurantId(),createReqVO.getRestaurantName());
        return cateringPlan.getId();
    }

    @Override
    public PageResult<CateringPlanDO> getCateringPlanPage(CateringPlanPageReqVO pageReqVO) {
        return cateringPlanMapper.selectPage(pageReqVO);
    }

    @Override
    public void updateCateringPlan(CateringPlanSaveReqVO updateReqVO) {
        CateringPlanDO cateringPlan = cateringPlanMapper.selectById(updateReqVO.getId());
        BeanUtils.copyProperties(updateReqVO, cateringPlan);
        cateringPlanMapper.updateById(cateringPlan);
        //先删除在创建
        cateringPlanItemMapper.delete(new LambdaQueryWrapperX<CateringPlanItemDO>()
                .eq(CateringPlanItemDO::getCateringPlanId, updateReqVO.getId()));

        List<CateringPlanItemDO> items = updateReqVO.getItems();
        for (CateringPlanItemDO item : items) {
            item.setCateringPlanId(cateringPlan.getId());
            cateringPlanItemMapper.insert(item);
        }
        invokeWechatInterface(updateReqVO.getRestaurantId(),updateReqVO.getRestaurantName());
    }

    @Override
    public CateringPlanRespVO getCateringPlan(Long id) {
        CateringPlanDO cateringPlan = cateringPlanMapper.selectById(id);

        List<CateringPlanItemDO> cateringPlanItems = cateringPlanItemMapper
                .selectList(new LambdaQueryWrapperX<CateringPlanItemDO>()
                        .eq(CateringPlanItemDO::getCateringPlanId, cateringPlan.getId()));

        CateringPlanRespVO respVO = new CateringPlanRespVO();
        BeanUtils.copyProperties(cateringPlan, respVO);
        respVO.setItems(cateringPlanItems);
        return respVO;
    }

    @Override
    public void deleteCateringPlan(Long[] ids) {
        for (Long id : ids) {
            cateringPlanMapper.deleteById(id);
        }
    }

    @Override
    public List<CateringPlanItemDO> exportCateringPlanItemExcel(Long id) {
        CateringPlanDO cateringPlan = cateringPlanMapper.selectById(id);
        return cateringPlanItemMapper.selectList(new LambdaQueryWrapperX<CateringPlanItemDO>()
                .eq(CateringPlanItemDO::getCateringPlanId, cateringPlan.getId()));
    }

    @Override
    public CateringPlanRespVO exportCateringPlanItemExcelDate(Long id) {
        CateringPlanRespVO respVO = new CateringPlanRespVO();
        CateringPlanDO cateringPlan = cateringPlanMapper.selectById(id);
        BeanUtils.copyProperties(cateringPlan, respVO);
        List<CateringPlanItemDO> cateringPlanItems = cateringPlanItemMapper
                .selectList(new LambdaQueryWrapperX<CateringPlanItemDO>()
                        .eq(CateringPlanItemDO::getCateringPlanId, cateringPlan.getId()));

        for (CateringPlanItemDO cateringPlanItem : cateringPlanItems) {
            String monday = cateringPlanItem.getMonday();
            List<String> mondayList = JSONArray.parseArray(monday, String.class);
            if (CollectionUtil.isNotEmpty(mondayList)) {
                for (int i = 0; i < mondayList.size(); i++) {
                    DishesDO dishesDO = dishesMapper.selectOne(new LambdaQueryWrapperX<DishesDO>().eq(DishesDO::getFoodName, mondayList.get(i)));
                    mondayList.set(i,mondayList.get(i) + "-" + dishesDO.getPrice().setScale(2) + "元");
                }
            }

            List<String> tuesdayList = JSONArray.parseArray(cateringPlanItem.getTuesday(), String.class);
            if (CollectionUtil.isNotEmpty(tuesdayList)) {
                for (int i = 0; i < tuesdayList.size(); i++) {
                    DishesDO dishesDO = dishesMapper.selectOne(new LambdaQueryWrapperX<DishesDO>().eq(DishesDO::getFoodName, tuesdayList.get(i)));
                    tuesdayList.set(i,tuesdayList.get(i) + "-" + dishesDO.getPrice().setScale(2) + "元");
                }
            }


            List<String> wednesdayList = JSONArray.parseArray(cateringPlanItem.getWednesday(), String.class);
            if (CollectionUtil.isNotEmpty(wednesdayList)) {
                for (int i = 0; i < wednesdayList.size(); i++) {
                    DishesDO dishesDO = dishesMapper.selectOne(new LambdaQueryWrapperX<DishesDO>().eq(DishesDO::getFoodName, wednesdayList.get(i)));
                    wednesdayList.set(i,wednesdayList.get(i) + "-" + dishesDO.getPrice().setScale(2) + "元");
                }
            }

            List<String> thursdayList = JSONArray.parseArray(cateringPlanItem.getThursday(), String.class);
            if (CollectionUtil.isNotEmpty(thursdayList)) {
                for (int i = 0; i < thursdayList.size(); i++) {
                    DishesDO dishesDO = dishesMapper.selectOne(new LambdaQueryWrapperX<DishesDO>().eq(DishesDO::getFoodName, thursdayList.get(i)));
                    thursdayList.set(i,thursdayList.get(i) + "-" + dishesDO.getPrice().setScale(2) + "元");
                }
            }

            List<String> fridayList = JSONArray.parseArray(cateringPlanItem.getFriday(), String.class);
            if (CollectionUtil.isNotEmpty(fridayList)) {
                for (int i = 0; i < fridayList.size(); i++) {
                    DishesDO dishesDO = dishesMapper.selectOne(new LambdaQueryWrapperX<DishesDO>().eq(DishesDO::getFoodName, fridayList.get(i)));
                    fridayList.set(i,fridayList.get(i) + "-" + dishesDO.getPrice().setScale(2) + "元");
                }
            }

            List<String> saturdayList = JSONArray.parseArray(cateringPlanItem.getSaturday(), String.class);
            if (CollectionUtil.isNotEmpty(saturdayList)) {
                for (int i = 0; i < saturdayList.size(); i++) {
                    DishesDO dishesDO = dishesMapper.selectOne(new LambdaQueryWrapperX<DishesDO>().eq(DishesDO::getFoodName, saturdayList.get(i)));
                    saturdayList.set(i,saturdayList.get(i) + "-" + dishesDO.getPrice().setScale(2) + "元");
                }
            }
            List<String> sundayList = JSONArray.parseArray(cateringPlanItem.getSunday(), String.class);
            if (CollectionUtil.isNotEmpty(sundayList)) {
                for (int i = 0; i < sundayList.size(); i++) {
                    DishesDO dishesDO = dishesMapper.selectOne(new LambdaQueryWrapperX<DishesDO>().eq(DishesDO::getFoodName, sundayList.get(i)));
                    sundayList.set(i,sundayList.get(i) + "-" + dishesDO.getPrice().setScale(2) + "元");
                }
            }
            cateringPlanItem.setMonday(String.join(",", mondayList));
            cateringPlanItem.setTuesday(String.join(",", tuesdayList));
            cateringPlanItem.setThursday(String.join(",", thursdayList));
            cateringPlanItem.setWednesday(String.join(",", wednesdayList));
            cateringPlanItem.setFriday(String.join(",", fridayList));
            cateringPlanItem.setSaturday(String.join(",", saturdayList));
            cateringPlanItem.setSunday(String.join(",", sundayList));
        }
        respVO.setItems(cateringPlanItems);
        return respVO;
    }

    @Override
    public void exportImportTemplate(HttpServletResponse response) throws IOException {
        List<List<String>> data = buildImportTemplateSampleData();
        ExcelUtils.setResponseProperties(response, "餐饮计划导入模板.xlsx");
        try (java.io.OutputStream outputStream = response.getOutputStream()) {
            EasyExcel.write(outputStream)
                    .sheet("模板")
                    .doWrite(data);
            outputStream.flush();
        }
    }

    @Override
    @Transactional
    public ImportResultVO importCateringPlan(MultipartFile file, Long restaurantId) throws Exception {
        List<Map<Integer, String>> rawRows = new ArrayList<>();
        EasyExcel.read(file.getInputStream(), new AnalysisEventListener<Map<Integer, String>>() {
                    @Override
                    public void invoke(Map<Integer, String> data, AnalysisContext context) {
                        rawRows.add(data);
                    }

                    @Override
                    public void doAfterAllAnalysed(AnalysisContext context) {
                    }
                })
                .autoCloseStream(true)
                .sheet(0)
                .headRowNumber(0)
                .doRead();

        int success = 0;
        List<ImportResultVO.RowError> failures = new ArrayList<>();

        if (rawRows.isEmpty()) {
            return ImportUtil.buildResult(success, failures);
        }

        Map<String, String> meta = new HashMap<>();
        List<CateringPlanItemDO> items = new ArrayList<>();
        for (int i = 0; i < rawRows.size(); i++) {
            Map<Integer, String> row = rawRows.get(i);
            String col0 = getCell(row, 0);
            String col1 = getCell(row, 1);
            String nextCol0 = i+1 >= rawRows.size() ? "" :getCell(rawRows.get(i+1), 0);
            if (i < 4) {
                if (col0 != null) {
                    meta.put(col0.trim(), col1);
                }
                continue;
            }
            if(i == 4){
                continue;
            }
            if (col0 == null && col1 == null) {
                continue;
            }
            CateringPlanItemDO item = new CateringPlanItemDO();
            item.setTenantId(TenantContextHolder.getTenantId());
            item.setMealTimes(col0);
            item.setMonday(parseDishes(col1));
            item.setTuesday(parseDishes(getCell(row, 2)));
            item.setWednesday(parseDishes(getCell(row, 3)));
            item.setThursday(parseDishes(getCell(row, 4)));
            item.setFriday(parseDishes(getCell(row, 5)));
            item.setSaturday(parseDishes(getCell(row, 6)));
            item.setSunday(parseDishes(getCell(row, 7)));
            items.add(item);
            if (isOptionalMeal(nextCol0)) {
                Map<Integer, String> nextRow = rawRows.get(i + 1);
                item.setMondayExtra(parseDishes(getCell(nextRow, 1)));
                item.setTuesdayExtra(parseDishes(getCell(nextRow, 2)));
                item.setWednesdayExtra(parseDishes(getCell(nextRow, 3)));
                item.setThursdayExtra(parseDishes(getCell(nextRow, 4)));
                item.setFridayExtra(parseDishes(getCell(nextRow, 5)));
                item.setSaturdayExtra(parseDishes(getCell(nextRow, 6)));
                item.setSundayExtra(parseDishes(getCell(nextRow, 7)));
                i++;
            }
        }

        if (items.isEmpty()) {
            ImportUtil.addFailure(failures, 7, null, null, "未读取到餐次明细");
            return ImportUtil.buildResult(success, failures);
        }

        CateringPlanDO cateringPlan = buildCateringPlan(meta, restaurantId, failures);
        if (!failures.isEmpty()) {
            return ImportUtil.buildResult(success, failures);
        }

        ensureDishesExists(items, cateringPlan, failures);
        if (!failures.isEmpty()) {
            return ImportUtil.buildResult(success, failures);
        }

        cateringPlanMapper.insert(cateringPlan);
        for (CateringPlanItemDO item : items) {
            item.setCateringPlanId(cateringPlan.getId());
            cateringPlanItemMapper.insert(item);
        }

        invokeWechatInterface(cateringPlan.getRestaurantId(), cateringPlan.getRestaurantName());
        success = 1;
        return ImportUtil.buildResult(success, failures);
    }

    private LocalDate parseDate(String value, List<ImportResultVO.RowError> failures) {
        List<DateTimeFormatter> patterns = Arrays.asList(
                DateTimeFormatter.ofPattern("yyyy/M/d"),
                DateTimeFormatter.ofPattern("yyyy-MM-dd"),
                DateTimeFormatter.ofPattern("yyyy/MM/dd"),
                DateTimeFormatter.ofPattern("yyyy.M.d")
        );
        for (DateTimeFormatter formatter : patterns) {
            try {
                return LocalDate.parse(value, formatter);
            } catch (DateTimeParseException ignored) {
            }
        }
        ImportUtil.addFailure(failures, 1, null, null, "计划开始日期格式不正确");
        return null;
    }

    private String getCell(Map<Integer, String> row, int index) {
        String value = row.get(index);
        return value != null ? value.trim() : null;
    }

    /**
     * 格式化菜品字符串
     * @param dishes
     * @return
     */
    private String parseDishes(String dishes){
        if(StringUtils.isBlank(dishes)){
            return "";
        }
        return new Gson().toJson(dishes.split("、"));
    }

    private List<List<String>> buildImportTemplateSampleData() {
        List<List<String>> data = new ArrayList<>();
        data.add(Arrays.asList("计划开始日期", ""));
        data.add(Arrays.asList("计划名称", ""));
        data.add(Arrays.asList("餐谱说明", ""));
        data.add(Arrays.asList("上墙备注", ""));
        data.add(Arrays.asList("餐次", "周一", "周二", "周三", "周四", "周五", "周六", "周日"));
        data.add(Arrays.asList("早餐", "", "", "", "", "", "", ""));
        data.add(Arrays.asList("中餐", "", "", "", "", "", "", ""));
        data.add(Arrays.asList("中餐（可选）", "", "", "", "", "", "", ""));
        data.add(Arrays.asList("晚餐", "", "", "", "", "", "", ""));
        return data;
    }


    private void invokeWechatInterface(Long restaurantId,String restaurantName){
        // 调用外部接口更新餐厅周菜单
        try {
            JSONObject requestJson = new JSONObject();
            requestJson.put("tenantId", TenantContextHolder.getTenantId());
            requestJson.put("restaurantId", restaurantId);
            requestJson.put("restaurantName", restaurantName);

            String url = "http://home.ynims.com:80/api/eldercare/event/updateRestaurantWeeklyMenu?tenantId=" + TenantContextHolder.getTenantId()
                    + "&restaurantId="+restaurantId+"&restaurantName="+restaurantName;
            String jsonParam = JSON.toJSONString(requestJson);

            HttpClientResult httpClientResult = HttpClientUtil.doPostJson(url, jsonParam, null);
            // 可以根据需要处理返回结果
            if (httpClientResult.getCode() != 200) {
                // 记录日志或处理错误
                log.error("调用更新餐厅周菜单接口失败，状态码：{}，返回内容：{}", httpClientResult.getCode(), httpClientResult.getContent());
            } else {
                log.info("调用更新餐厅周菜单接口成功，参数：{}", jsonParam);
            }
        } catch (Exception e) {
            // 记录异常日志，但不影响主流程
            log.error("调用更新餐厅周菜单接口异常：{}", e.getMessage(), e);
        }
    }

    private CateringPlanDO buildCateringPlan(Map<String, String> meta, Long restaurantId, List<ImportResultVO.RowError> failures) {
        String startDateStr = meta.get("计划开始日期");
        String planName = meta.get("计划名称");
        String description = meta.get("餐谱说明");
        String remarks = meta.get("上墙备注");

        if (startDateStr == null) {
            ImportUtil.addFailure(failures, 1, null, null, "计划开始日期不能为空");
        }
        if (planName == null) {
            ImportUtil.addFailure(failures, 2, null, null, "计划名称不能为空");
        }
        if (!failures.isEmpty()) {
            return null;
        }

        LocalDate startDate = parseDate(startDateStr.trim(), failures);
        if (startDate == null) {
            return null;
        }

        CateringPlanDO cateringPlan = new CateringPlanDO();
        cateringPlan.setRestaurantId(restaurantId);
        RestaurantManagementDO restaurant = restaurantManagementMapper.selectById(restaurantId);
        if (restaurant != null) {
            cateringPlan.setRestaurantName(restaurant.getRestaurantName());
        }
        cateringPlan.setScheduledStartDate(startDate);
        cateringPlan.setScheduledEndDate(startDate.plusDays(6));
        cateringPlan.setPlanName(planName);
        cateringPlan.setDescription(description);
        cateringPlan.setRemarks(remarks);
        cateringPlan.setCreatedTime(new Date());
        cateringPlan.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
        cateringPlan.setTenantId(TenantContextHolder.getTenantId());
        return cateringPlan;
    }

    private void ensureDishesExists(List<CateringPlanItemDO> items, CateringPlanDO cateringPlan, List<ImportResultVO.RowError> failures) {
        Map<String, DishesDO> existing = dishesMapper.selectList(new LambdaQueryWrapperX<DishesDO>()
                        .eq(DishesDO::getTenantId, TenantContextHolder.getTenantId())
                        .eq(DishesDO::getRestaurantId, cateringPlan.getRestaurantId()))
                .stream()
                .collect(java.util.stream.Collectors.toMap(DishesDO::getFoodName, item -> item, (a, b) -> a));

        Set<String> toCreate = new LinkedHashSet<>();
        for (CateringPlanItemDO item : items) {
            collectDishes(item.getMonday(), toCreate, existing);
            collectDishes(item.getTuesday(), toCreate, existing);
            collectDishes(item.getWednesday(), toCreate, existing);
            collectDishes(item.getThursday(), toCreate, existing);
            collectDishes(item.getFriday(), toCreate, existing);
            collectDishes(item.getSaturday(), toCreate, existing);
            collectDishes(item.getSunday(), toCreate, existing);
            collectDishes(item.getMondayExtra(), toCreate, existing);
            collectDishes(item.getTuesdayExtra(), toCreate, existing);
            collectDishes(item.getWednesdayExtra(), toCreate, existing);
            collectDishes(item.getThursdayExtra(), toCreate, existing);
            collectDishes(item.getFridayExtra(), toCreate, existing);
            collectDishes(item.getSaturdayExtra(), toCreate, existing);
            collectDishes(item.getSundayExtra(), toCreate, existing);
        }

        for (String foodName : toCreate) {
            try {
                DishesDO dishes = new DishesDO();
                dishes.setFoodName(foodName);
                dishes.setRestaurantId(cateringPlan.getRestaurantId());
                dishes.setRestaurantName(cateringPlan.getRestaurantName());
                dishes.setTenantId(TenantContextHolder.getTenantId());
                dishes.setPrice(BigDecimal.ZERO);
                dishes.setStatus(1);
                dishes.setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname());
                dishes.setCreatedTime(new Date());
                dishesMapper.insert(dishes);
            } catch (Exception ex) {
                ImportUtil.addFailure(failures, 1, null, null, "自动创建菜品失败：" + foodName, ex);
            }
        }
    }

    private void collectDishes(String value, Set<String> toCreate, Map<String, DishesDO> existing) {
        if (value == null || value.isEmpty()) {
            return;
        }
        String normalized = value.replace("，", ",");
        for (String name : normalized.split(",")) {
            String trimmed = name.trim();
            if (trimmed.isEmpty()) {
                continue;
            }
            if (!existing.containsKey(trimmed)) {
                toCreate.add(trimmed);
            }
        }
    }

    private boolean isOptionalMeal(String mealTimes) {
        if (mealTimes == null) {
            return false;
        }
        String normalized = mealTimes.replace("（", "(").replace("）", ")");
        return normalized.contains("(可选)") || normalized.contains("可选");
    }
}
