package com.cf.imes.module.executor.util.fileConversion.admin.files.excel;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.nacos.common.utils.StringUtils;
import com.cf.imes.framework.organ.core.context.OrganContextHolder;
import com.cf.imes.module.executor.dal.dataobject.order.OrderDO;
import com.cf.imes.module.executor.dal.dataobject.plate.PlateGoodDO;
import com.cf.imes.module.executor.dal.mysql.plate.PlateGoodMapper;
import com.cf.imes.module.executor.enums.*;
import com.cf.imes.module.executor.util.fileConversion.admin.ToolUtil;
import com.cf.imes.module.system.api.dict.DictDataApi;
import com.cf.imes.module.system.api.dict.dto.DictDataRespDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import jakarta.annotation.Resource;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;

import static com.cf.imes.framework.organ.core.context.OrganContextHolder.setOrganId;

/**
 * Excel文件数据校验器
 */
@Slf4j
@Component
public class ExcelCheckListUtil {

    @Resource
    private DictDataApi dictDataApi;

    @Resource
    private PlateGoodMapper plateGoodMapper;

    private static final String ERR = "error";
    private static final String DEALER_PHONE = "dealerPhoneNumber";
    private static final String NUMBER_TYPE_ERR = "数量类型错误";
    private static final String NOT_EMPTY= "不可为空";
    private static final String NUMBER_ERR = "数量错误";
    private static final String ACCORD_ENUM = "请按照提示词语选择填入";
    private static final String POSITIVE_ERR = "数据需为正数";
    private static final String NUMBER_LENGTH_ERR = "数据长度过长";
    final String PLATE_NOT_EXIST = "当前对应的板材不存在，请在板材管理里创建对应板材再进行导入";

    // 校验文件订单数据, 当有错时返回Map<String,String>，当数据完全正确时，返回OrderDO
    public ExcelCheckVO checkExcelList(Map<String, String> orderMap) {

        AtomicBoolean error = new AtomicBoolean(true);

        // 非空判断
        setIfInvalid(ProduceExcelConstants.ORDER_CUSTOMER, "---< 客户不能为空 >---", orderMap, 255, error);
        setIfInvalid(ProduceExcelConstants.ORDER_SALESMAN, "---< 经销商不能为空 >---", orderMap, 255, error);
        setIfInvalid(ProduceExcelConstants.ORDER_ADDRESS, "---< 客户地址不能为空 >---", orderMap, 255, error);
        setIfInvalid(ProduceExcelConstants.ORDER_ADDRESS, "---< 客户地址不能为空 >---", orderMap, 255, error);

        if (orderMap.get(ProduceExcelConstants.ORDER_PHONE_NUMBER) == null) {
            orderMap.put(ProduceExcelConstants.ORDER_PHONE_NUMBER, "---< 客户电话不能为空 >---");
            error.set(false);
        } else {
            setPhone(ProduceExcelConstants.ORDER_PHONE_NUMBER, "---< 客户电话格式错误 >---", orderMap, error);
        }
        if (orderMap.get(ProduceExcelConstants.ORDER_DEALER_PHONE_NUMBER) != null) {
            setPhone(ProduceExcelConstants.ORDER_DEALER_PHONE_NUMBER, "---< 经销商电话格式错误 >---", orderMap, error);
        }
        LocalDate deliverDate = null;
        if (orderMap.get(ProduceExcelConstants.ORDER_DELIVERY_DATE) == null) {
            orderMap.put(ProduceExcelConstants.ORDER_DELIVERY_DATE, "---< 出货日期不能为空 >---");
            error.set(false);
        } else {
            String s = orderMap.get(ProduceExcelConstants.ORDER_DELIVERY_DATE);
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            Map<String, Object> mapDate = ToolUtil.changeDate(s, formatter);
            if (mapDate.get(ERR) instanceof Integer && (Integer) mapDate.get(ERR) == -1) {
                orderMap.put(ProduceExcelConstants.ORDER_DELIVERY_DATE, s + "---< 出货日期格式错误 >---");
                error.set(false);
            } else {
                deliverDate = (LocalDate) mapDate.get("date");
                if (deliverDate.isBefore(LocalDate.now())) {
                    deliverDate = LocalDate.now().plus(30, ChronoUnit.DAYS);
                }
            }
        }
        OrderDO orderDO = new OrderDO();
        if (error.get()) {
            orderDO = OrderDO.builder()
                    .organId(OrganContextHolder.getOrganId())
                    .dataType(DataTypeEnum.FILE_IMPORT.getStatus())
                    .orderDate(LocalDateTime.now())
                    .customer(orderMap.get(ProduceExcelConstants.ORDER_CUSTOMER))
                    .customOrderNo(orderMap.get(ProduceExcelConstants.ORDER_CUSTOM_ORDER_NO))
                    .dealer(orderMap.get(ProduceExcelConstants.ORDER_DEALER))
                    .dealerPhoneNumber(orderMap.get(ProduceExcelConstants.ORDER_DEALER_PHONE_NUMBER))
                    .address(orderMap.get(ProduceExcelConstants.ORDER_ADDRESS))
                    .phoneNumber(orderMap.get(ProduceExcelConstants.ORDER_PHONE_NUMBER))
                    .salesman(orderMap.get(ProduceExcelConstants.ORDER_SALESMAN))
                    .splitter(orderMap.get(ProduceExcelConstants.ORDER_SPLITTER))
                    .deliveryDate(deliverDate.atStartOfDay())
                    .orderType(OrderTypeEnum.MAIN_ORDER.getStatus())
                    .remark(orderMap.get(ProduceExcelConstants.ORDER_REMARK))
                    .build();
        }

        return new ExcelCheckVO().setFlag(error.get()).setOrderDO(orderDO).setMapOder(orderMap);
    }

    // order生产单校验电话号码
    private void setPhone(String key, String errValue, Map<String, String> orderMap, AtomicBoolean error) {
        if (!ToolUtil.validatePhoneNumber(orderMap.get(key))) {
            orderMap.put(key, orderMap.get(key) + errValue);
            error.set(false);
        }
    }

    private void setIfInvalid(String key, String errValue, Map<String, String> orderMap, int length, AtomicBoolean error) {
        orderMap.compute(key, (k, v) -> {
            if (v == null) {
                error.set(false);
                return errValue;
            } else {
                int size = (v).length();
                if (size > length) {
                    error.set(false);
                    return NUMBER_LENGTH_ERR;
                }
            }
            return v;
        });
    }


    // 校验文件生产数据
    public ExcelCheckVO checkExcelLargeFile(List<OrderPlateImportExcelVO> list) {
        List<OrderPlateImportExcelVO> resultList = new ArrayList<>();
        // 100条一分，确定分片多少
        int batchSize = 100;
        int batchCount = (list.size() + batchSize - 1) / batchSize;

        List<CompletableFuture<List<OrderPlateImportExcelVO>>> futures = new ArrayList<>();
        AtomicBoolean flag = new AtomicBoolean(true);

        Long organId = OrganContextHolder.getOrganId();

        // 遍历分片
        for (int i = 0; i < batchCount; i++) {
            int start = i * batchSize;
            int end = Math.min(start + batchSize, list.size());
            List<OrderPlateImportExcelVO> subList = list.subList(start, end);


//            CompletableFuture<List<OrderPlateImportExcelVO>> future = CompletableFuture.supplyAsync(() -> checkData(subList, flag));
            CompletableFuture<List<OrderPlateImportExcelVO>> future = CompletableFuture.supplyAsync(() -> {

                        setOrganId(organId);
                        return checkData(subList, flag);
                    });
            futures.add(future);
        }

        // 等待所有异步操作完成，并收集结果
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[batchCount])).join();

        for (CompletableFuture<List<OrderPlateImportExcelVO>> future : futures) {
            try {
                List<OrderPlateImportExcelVO> result = future.get(); // 获取每个任务的结果

                resultList.addAll(result);
            } catch (InterruptedException | ExecutionException e) {
                throw new RuntimeException(e);
            }
        }

        return new ExcelCheckVO().setFlag(flag.get()).setList(resultList);
    }

    // List数据校验
    private List<OrderPlateImportExcelVO> checkData(List<OrderPlateImportExcelVO> list, AtomicBoolean flag) {

        for (OrderPlateImportExcelVO vo : list) {
            List<String> errList = new ArrayList<>();
            // 全部需要值的字段
            // 检查非空 类型、数量
            isEmpty(vo.getGoodType(), errList, "类型",32, flag);
            isEmpty(vo.getGoodsNumber(), errList, "数量", 10000000 ,flag);

            // 检查商品物料编码或是商品名称、规格、材质、颜色不可全为空
            if (StringUtils.isEmpty(vo.getGoodsName())
                    ||  StringUtils.isEmpty(vo.getMaterial())
                    || StringUtils.isEmpty(vo.getColor())
                    || StringUtils.isEmpty(vo.getUnit())) {
                errList.add("商品商品名称、材质、颜色、单位不可为空");
                flag.set(false);
            }


            if (StringUtils.equals(vo.getGoodType(), "配件")) {
                // 复合部件必填
                isNotEmptyAndBack(vo.getIsComposite(), QuarrelEnum.class, errList, "是否复合部件", 1, flag); // 判断传入的值，是否符合字典中的枚举类
                // 配件类型必填
                isNotEmptyAndBack(vo.getCategory(), CategoryEnum.class, errList, "配件类型", 32, flag);
                // 配件长宽厚，存在判断是否可以转义
                isEmptyAndPositiveNumberSize(vo.getGoodsWidth(), Double.class, 5, 3, errList, "配件宽", flag);
                isEmptyAndPositiveNumberSize(vo.getGoodsHeight(), Double.class, 5, 3, errList, "配件高", flag);
                isEmptyAndPositiveNumberSize(vo.getGoodsThickness(), Double.class, 5, 3, errList, "配件厚", flag);
                // 配件类型为封边条的时候，需要填写配件宽厚
                if (StringUtils.equals(vo.getCategory(), CategoryEnum.EDGING.getValue())) { // 封边条必须有宽高
                    isNotEmptyAndBack(vo.getGoodsWidth(), errList, "封边条宽", flag);
                    isNotEmptyAndBack(vo.getGoodsThickness(), errList, "封边条厚", flag);
                }
                // 配件数量判断
                isEmptyAndPositiveNumber(vo.getGoodsNumber(), Double.class, errList, "配件数量", flag);
            }
            // 若商品类型为板材，需检查房间、柜体、板名称、成品长、成品宽、拆单长、拆单宽、厚、左封边、右封边、上封边、下封边、排版面、纹路、开门方向不能为空
            if (StringUtils.equals(vo.getGoodType(), "板材")) {
                checkPlate(vo, errList, flag);
            }


//            if (!flag.get()) {
//                isErr.getAndIncrement();
//            }
            JSONArray jsonArray = JSON.parseArray(JSON.toJSONString(errList));// 错误列表
            vo.setResult(String.valueOf(jsonArray));

        }

        return list;
    }

    // 非空判断，错误填入
    private void isEmpty(String value, List<String> errList, String errValue, int length, AtomicBoolean flag) {
        if (StringUtils.isEmpty(value)) {
            errList.add(errValue + NOT_EMPTY);
            flag.set(false);
        } else {
            // 字符串长度检查
            int size = value.length();
            if (size > length) {
                errList.add(errValue + NUMBER_LENGTH_ERR);
                flag.set(false);
            }
        }
    }

    // 错误判断通用方法
    // 判断非零，错误返回；不为空时，判断是否符合枚举类
    private void isNotEmptyAndBack(String value, Class<? extends Enum<?>> enumClass, List<String> errList, String errValue, int length , AtomicBoolean flag) {
        if (StringUtils.isEmpty(value)) {
            errList.add(errValue + NOT_EMPTY);
            flag.set(false);
        } else {
            isNotEmptyToBack(value, enumClass, errList, errValue, length , flag);
        }
    }

    // 非空，判断判断是否符合枚举类
    private void isNotEmptyToBack(String value, Class<? extends Enum<?>> enumClass, List<String> errList, String errValue, int length , AtomicBoolean flag) {
        if (!StringUtils.isEmpty(value)) {
            isEmptyAndSize(value, length, errList, errValue, flag);
            try {
                // 获取枚举类的所有值
                Enum<?>[] enums = enumClass.getEnumConstants();
                boolean isValid = false;
                for (Enum<?> enumValue : enums) {
                    Field valueField = enumValue.getClass().getDeclaredField("value");
                    valueField.setAccessible(true);
                    String enumValueStr = (String) valueField.get(enumValue);
                    if (enumValueStr.equals(value)) {
                        isValid = true;
                        break;
                    }
                }
                if (!isValid) {
                    errList.add(errValue + ACCORD_ENUM);
                    flag.set(false);
                }
            } catch (IllegalArgumentException e) {
                errList.add(errValue + ACCORD_ENUM);
                flag.set(false);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
    }

    // 非零判断，有值判断---判断数值类型，数值长度
    private void isEmptyAndPositiveNumberSize(String value, Class<?> targetType, Integer length, Integer decimalLength, List<String> errList, String errValue, AtomicBoolean flag) {
        if (StringUtils.isNotEmpty(value)) {
            if (Boolean.TRUE.equals(ToolUtil.convertToType(value, targetType))) {
                if (Boolean.TRUE.equals(ToolUtil.checkNumber(value))) {
                    if (!ToolUtil.checkPrecision(value, length, decimalLength)) {
                        errList.add(errValue + NUMBER_LENGTH_ERR);
                        flag.set(false);
                    }
                } else {
                    errList.add(errValue + POSITIVE_ERR);
                    flag.set(false);
                }
            } else {
                errList.add(errValue + NUMBER_TYPE_ERR);
                flag.set(false);
            }
        }
    }

    // 判断非零，错误返回
    private void isNotEmptyAndBack(String value, List<String> errList, String errValue, AtomicBoolean flag) {
        if (StringUtils.isEmpty(value)) {
            errList.add(errValue + NOT_EMPTY);
            flag.set(false);
        }
    }

    // 判断非空，有值判断字符串的长度
    private void isEmptyAndSize(String value, Integer length, List<String> errList, String errValue, AtomicBoolean flag) {
        if (StringUtils.isNotEmpty(value)) {
            int size = value.length();
            if (size > length) {
                errList.add(errValue + NUMBER_LENGTH_ERR);
                flag.set(false);
            }
        }
    }

    // 判断非空，大于零判断
    public void isEmptyAndPositiveNumber(String value, Class<?> targetType, List<String> errList, String errValue, AtomicBoolean flag) {
        if (StringUtils.isNotEmpty(value)) {
            if (Boolean.FALSE.equals(ToolUtil.convertToType(value, targetType))) {
                errList.add(errValue + NUMBER_TYPE_ERR);
                flag.set(false);
            }else {
                if (Boolean.FALSE.equals(checkNumber(value))) {
                    errList.add(errValue + "数量不能为0");
                    flag.set(false);
                }
            }
        }
    }

    // 板材的数据以及长度的检验
    public void checkPlate(OrderPlateImportExcelVO vo, List<String> errList , AtomicBoolean flag) {
        // 数量类型判断
        isEmptyAndPositiveNumber(vo.getGoodsNumber(), Integer.class, errList, "板材数量", flag);
        // 板材纹理 默认有，不需要必填
        isNotEmptyToBack(StringUtils.isNotEmpty(vo.getWave()) ? vo.getWave() : "有", IsHavaeEnum.class, errList, "板材纹理", 4, flag);
        // 大板的长宽厚存在以及是否符合数个
//        isNotEmptyAndBack(vo.getGoodsWidth(), errList, "板材长度", flag);
        isEmptyAndPositiveNumberSize(vo.getGoodsWidth(), Double.class, 5, 3, errList, "板材长度", flag);
//        isNotEmptyAndBack(vo.getGoodsHeight(), errList, "板材宽度", flag);
        isEmptyAndPositiveNumberSize(vo.getGoodsHeight(), Double.class, 5, 3, errList, "板材宽度", flag);
        isNotEmptyAndBack(vo.getGoodsThickness(), errList, "板材厚度", flag);
        isEmptyAndPositiveNumberSize(vo.getGoodsThickness(), Double.class, 5, 3, errList, "板材厚度", flag);
        // 房间、柜体、板名称 不可以为空
        isEmptyAndSize(vo.getRoomsName(), 64, errList, "房间名称" , flag);
        isEmptyAndSize(vo.getCabinetsName(), 64, errList, "柜体名称", flag);
        isEmpty(vo.getName(), errList, "板材名称", 64, flag);
        //成品长、成品宽、拆单长、拆单宽、厚、左封边、右封边、上封边、下封边 不能为空
        isNotEmptyAndBack(vo.getHeight(), errList, "成品长度", flag);
        isEmptyAndPositiveNumberSize(vo.getHeight(), Double.class, 5, 3, errList, "成品长度", flag);
        isNotEmptyAndBack(vo.getWidth(), errList, "成品宽度", flag);
        isEmptyAndPositiveNumberSize(vo.getWidth(), Double.class, 5, 3, errList, "成品宽度", flag);
        isNotEmptyAndBack(vo.getSplitHeight(), errList, "拆单长度", flag);
        isEmptyAndPositiveNumberSize(vo.getSplitHeight(), Double.class, 5, 3, errList, "拆单长度", flag);
        isNotEmptyAndBack(vo.getSplitWidth(), errList, "拆单宽度", flag);
        isEmptyAndPositiveNumberSize(vo.getSplitWidth(), Double.class, 5, 3, errList, "拆单宽度", flag);
        isNotEmptyAndBack(vo.getThickness(), errList, "厚度", flag);
        isEmptyAndPositiveNumberSize(vo.getThickness(), Double.class, 5, 3, errList, "厚度", flag);
        isNotEmptyAndBack(vo.getSealLeft(), errList, "左封边", flag);
        isEmptyAndPositiveNumberSize(vo.getSealLeft(), Double.class, 5, 3, errList, "左封边", flag);
        isNotEmptyAndBack(vo.getSealRight(), errList, "右封边", flag);
        isEmptyAndPositiveNumberSize(vo.getSealRight(), Double.class, 5, 3, errList, "右封边", flag);
        isNotEmptyAndBack(vo.getSealUp(), errList, "上封边", flag);
        isEmptyAndPositiveNumberSize(vo.getSealUp(), Double.class, 5, 3, errList, "上封边", flag);
        isNotEmptyAndBack(vo.getSealDown(), errList, "下封边", flag);
        isEmptyAndPositiveNumberSize(vo.getSealDown(), Double.class, 5, 3, errList, "下封边", flag);
        isPlateExists(vo, errList, flag);
        // 排版面、纹路、开门方向
        isNotEmptyAndDict(vo.getTypographicFace(), DictTypeConstants.TYPOGRAPHY_TYPE, errList, "排版面", flag);
        isNotEmptyAndDict(vo.getTexture(), DictTypeConstants.GRAIN_TYPE, errList, "纹路", flag);
        isNotEmptyAndDict(vo.getOpenDoorType(), DictTypeConstants.DOOR_OPENING_DIRECTIONS, errList, "开门方向", flag);

    }

    // 板件是否存在判断，优先goodsId，没有判断（名称，材料，颜色，厚度，品牌（存在判断））
    private void isPlateExists(OrderPlateImportExcelVO plate, List<String> errList, AtomicBoolean flag) {
        PlateGoodDO plateGoodDO = null;
        List<PlateGoodDO> plateGoodDOs;
        if (StringUtils.isNotEmpty(plate.getGoodsId())) { // 判断 goodsId
            plateGoodDOs = plateGoodMapper.selectGoodOne(plate.getGoodsId(), OrganContextHolder.getOrganId());
        } else { // 判断（名称，材料，颜色，后端，品牌（存在判断））
            plateGoodDOs = plateGoodMapper.selectGood(plate.getMaterial(), plate.getColor(),
                    BigDecimal.valueOf(Long.parseLong(plate.getThickness())),
                    plate.getBrand(),plate.getGoodsName(), OrganContextHolder.getOrganId());
        }

        if (!plateGoodDOs.isEmpty()) {
            plateGoodDO = plateGoodDOs.get(0);
        }

        if (plateGoodDO == null) {
            errList.add(PLATE_NOT_EXIST);
            flag.set(false);
        }

    }

    // 判断非空，字典值判断
    public void isNotEmptyAndDict(String value, String dictType, List<String> errList, String errValue, AtomicBoolean flag) {
        if (StringUtils.isEmpty(value)) {
            errList.add(errValue + NOT_EMPTY);
            flag.set(false);
        } else {
            DictDataRespDTO dictDataRespDTO = dictDataApi.parseDictData(dictType, value).getData();
            if (dictDataRespDTO == null) {
                errList.add(errValue + ACCORD_ENUM);
                flag.set(false);
            }
        }
    }

    //    数量大小判断
    public Boolean checkNumber(String numberStr) {
        if (numberStr == null) {
            return false;
        }
        if (Double.parseDouble(numberStr) <= 0) {
            return false;
        }
        return true;
    }
}
