package cn.estsh.i3plus.ext.mes.pcn.apiservice.serviceimpl.busi;

import cn.estsh.i3plus.ext.mes.pcn.api.busi.ISxNumberRuleCheckAttributeService;
import cn.estsh.i3plus.ext.mes.pcn.pojo.bean.MesNumberRuleExt;
import cn.estsh.i3plus.ext.mes.pcn.pojo.bean.MesPartExt;
import cn.estsh.i3plus.ext.mes.pcn.pojo.util.MesPcnExtConstWords;
import cn.estsh.impp.framework.boot.exception.ImppBusiException;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: wangjie
 * @CreateDate: 2019/8/23 1:16 PM
 * @Description: 根据编码规则校验编码是否有效
 **/

@Service
public class SxNumberRuleCheckAttributeService implements ISxNumberRuleCheckAttributeService {

    @Override
    public Map<String, Object> doCheckNumberRule(Map<String, Object> map) {

        MesNumberRuleExt numberRuleDb = (MesNumberRuleExt) map.get(MesPcnExtConstWords.NUMBER_RULE_EXT);

        try {
            return doCheck(map, numberRuleDb);
        } catch (Exception e) {
            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put(MesPcnExtConstWords.RESULT, false);
            return resultMap;
        }
    }

    private Map<String, Object> doCheck(Map<String, Object> map, MesNumberRuleExt numberRuleDb) throws Exception {
        Method method = this.getClass().getDeclaredMethod(numberRuleDb.getMethodName(), Map.class, MesNumberRuleExt.class);
        method.setAccessible(true);
        return (Map<String, Object>) method.invoke(this, map, numberRuleDb);
    }

    private List<String> getSerialNumberList(String serialNumber, List<String> ruleLengthListStr, List<Integer> ruleLengthList) {
        List<String> serialNumberList = new ArrayList<>();
        Integer begin = 0;
        Integer end;
        if (!CollectionUtils.isEmpty(ruleLengthList)) {
            for (Integer length : ruleLengthList) {
                end = begin + length;
                serialNumberList.add(serialNumber.substring(begin, end));
                begin = end;
            }
        } else {
            for (String length : ruleLengthListStr) {
                if (length.equals(MesPcnExtConstWords.J)) {
                    String serialFormJStart = serialNumber.substring(begin);
                    Integer indexOneSpace = serialFormJStart.indexOf(MesPcnExtConstWords.ONE_SPACE);
                    if (indexOneSpace == -1) {
                        throw new ImppBusiException();
                    }
                    end = begin + indexOneSpace;
                    serialNumberList.add(serialFormJStart.substring(0, indexOneSpace));
                    ruleLengthList.add(indexOneSpace);
                    begin = end;
                } else {
                    end = begin + Integer.valueOf(length);
                    if (serialNumber.length() < end) {
                        throw new ImppBusiException();
                    }
                    serialNumberList.add(serialNumber.substring(begin, end));
                    ruleLengthList.add(Integer.valueOf(length));
                    begin = end;
                }
            }
        }
        return serialNumberList;
    }

    private Map<String, Object> commonCheck(Map<String, Object> map, MesNumberRuleExt numberRuleDb, List<String> dynamicRuleList) {
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put(MesPcnExtConstWords.RESULT, false);
        String serialNumber = (String) map.get(MesPcnExtConstWords.SERIAL_NUMBER);
        MesPartExt partExt = (MesPartExt) map.get(MesPcnExtConstWords.PART_EXT);

        List<String> numberRuleList = Arrays.asList(
                numberRuleDb.getNumberRule().replace(MesPcnExtConstWords.LEFT_BRACE, MesPcnExtConstWords.EMPTY).split(MesPcnExtConstWords.RIGHT_BRACE));
        numberRuleList = numberRuleList.stream().map(o -> o.toUpperCase()).collect(Collectors.toList());

        String ruleLengthSpilt = numberRuleDb.getRuleLengthSpilt();
        if (ruleLengthSpilt.contains(MesPcnExtConstWords.P)) {
            ruleLengthSpilt = ruleLengthSpilt.replaceAll(MesPcnExtConstWords.P, String.valueOf(partExt.getPartNo().length()));
        }
        if (ruleLengthSpilt.contains(MesPcnExtConstWords.L)) {
            ruleLengthSpilt = ruleLengthSpilt.replaceAll(MesPcnExtConstWords.L, StringUtils.isEmpty(partExt.getPartLotNoLength()) ? "0" : String.valueOf(partExt.getPartLotNoLength()));
        }

        List<String> ruleLengthListStr = Arrays.asList(ruleLengthSpilt.split(MesPcnExtConstWords.COMMA));
        List<Integer> ruleLengthList = new ArrayList<>();

        List<String> spiltRuleList = StringUtils.isEmpty(numberRuleDb.getSpiltRule()) ? null :
                new ArrayList<>(Arrays.asList(numberRuleDb.getSpiltRule().split(MesPcnExtConstWords.COMMA)));

        if (!ruleLengthSpilt.contains(MesPcnExtConstWords.J)) {

            ruleLengthList = ruleLengthListStr.stream().map(Integer::valueOf).collect(Collectors.toList());

            Integer length = ruleLengthList.stream().reduce(Integer::sum).orElse(0);

            if (Integer.valueOf(serialNumber.length()).compareTo(length) != 0) {
                return resultMap;
            }

        }

        try {

            List<String> serialNumberList = getSerialNumberList(serialNumber, ruleLengthListStr, ruleLengthList);

            if (ruleLengthSpilt.contains(MesPcnExtConstWords.J)) {

                Integer length = ruleLengthList.stream().reduce(Integer::sum).orElse(0);

                if (Integer.valueOf(serialNumber.length()).compareTo(length) != 0) {
                    return resultMap;
                }

            }

            int snIndex = 0;
            for (String s : numberRuleList) {
                switch (s) {
                    case MesPcnExtConstWords.PREFIX_BIG:
                        if (!serialNumberList.get(0).equals(numberRuleDb.getPrefix()) ||
                                Integer.valueOf(serialNumberList.get(0).length()).compareTo(ruleLengthList.get(0)) != 0) {
                            throw new ImppBusiException();
                        }
                        snIndex += ruleLengthList.get(0);
                        serialNumberList.remove(0);
                        ruleLengthList.remove(0);
                        break;
                    case MesPcnExtConstWords.PARTNO:
                        if (null == partExt || !serialNumberList.get(0).equals(partExt.getPartNo()) ||
                                Integer.valueOf(serialNumberList.get(0).length()).compareTo(ruleLengthList.get(0)) != 0) {
                            throw new ImppBusiException();
                        }
                        snIndex += ruleLengthList.get(0);
                        serialNumberList.remove(0);
                        ruleLengthList.remove(0);
                        break;
                    case MesPcnExtConstWords.YEAR:
                    case MesPcnExtConstWords.MONTH:
                    case MesPcnExtConstWords.DAY:
                    case MesPcnExtConstWords.HOUR:
                    case MesPcnExtConstWords.MINUTE:
                    case MesPcnExtConstWords.SECOND:
                        //---兼容EP----
                    case MesPcnExtConstWords.J://日文
                    case MesPcnExtConstWords.SUPPLIERRULE:
                        if (Integer.valueOf(serialNumberList.get(0).length()).compareTo(ruleLengthList.get(0)) != 0) {
                            throw new ImppBusiException();
                        }
                        snIndex += ruleLengthList.get(0);
                        serialNumberList.remove(0);
                        ruleLengthList.remove(0);
                        break;
                    case MesPcnExtConstWords.L: //变更号
                        if (Integer.valueOf(serialNumberList.get(0).length()).compareTo(ruleLengthList.get(0)) != 0) {
                            throw new ImppBusiException();
                        }
                        snIndex += ruleLengthList.get(0);
                        resultMap.put(MesPcnExtConstWords.L, serialNumberList.get(0));
                        serialNumberList.remove(0);
                        ruleLengthList.remove(0);
                        break;
                    case MesPcnExtConstWords.N: //数量
                        if (Integer.valueOf(serialNumberList.get(0).length()).compareTo(ruleLengthList.get(0)) != 0) {
                            throw new ImppBusiException();
                        }
                        snIndex += ruleLengthList.get(0);
                        resultMap.put(MesPcnExtConstWords.N, serialNumberList.get(0));
                        serialNumberList.remove(0);
                        ruleLengthList.remove(0);
                        break;
                    case MesPcnExtConstWords.SERIALNO:
                        if (Integer.valueOf(serialNumberList.get(0).length()).compareTo(ruleLengthList.get(0)) != 0) {
                            throw new ImppBusiException();
                        }
                        resultMap.put(MesPcnExtConstWords.SEQNO, serialNumberList.get(0));
                        StringBuffer snWithoutSeqNoBuffer = new StringBuffer(serialNumber);
                        if(resultMap.containsKey(MesPcnExtConstWords.SN_WITHOUT_SEQNO)){
                            String snWithoutSeqNo = String.valueOf(resultMap.get(MesPcnExtConstWords.SN_WITHOUT_SEQNO));
                            snWithoutSeqNoBuffer = new StringBuffer(snWithoutSeqNo);
                        }

                        resultMap.put(MesPcnExtConstWords.SN_WITHOUT_SEQNO, snWithoutSeqNoBuffer.replace(snIndex, snIndex + ruleLengthList.get(0), MesPcnExtConstWords.SEQNO));

                        snIndex += ruleLengthList.get(0);
                        serialNumberList.remove(0);
                        ruleLengthList.remove(0);
                        break;
                    case MesPcnExtConstWords.SERIALNO2:
                        if (Integer.valueOf(serialNumberList.get(0).length()).compareTo(ruleLengthList.get(0)) != 0) {
                            throw new ImppBusiException();
                        }
                        snIndex += ruleLengthList.get(0);
                        resultMap.put(MesPcnExtConstWords.SEQNO2, serialNumberList.get(0));
                        if(resultMap.containsKey(MesPcnExtConstWords.SN_WITHOUT_SEQNO)){
                            String snWithoutSeqNo = String.valueOf(resultMap.get(MesPcnExtConstWords.SN_WITHOUT_SEQNO));
                            resultMap.put(MesPcnExtConstWords.SN_WITHOUT_SEQNO, snWithoutSeqNo.replaceFirst(serialNumberList.get(0), MesPcnExtConstWords.SEQNO2));
                        }else{
                            resultMap.put(MesPcnExtConstWords.SN_WITHOUT_SEQNO, serialNumber.replaceFirst(serialNumberList.get(0), MesPcnExtConstWords.SEQNO2));
                        }
                        serialNumberList.remove(0);
                        ruleLengthList.remove(0);
                        break;
                    //---兼容EP----
                    case MesPcnExtConstWords.SPILTRULE:
                        //配置空格使用"/u0020"
                        if (CollectionUtils.isEmpty(spiltRuleList) || !serialNumberList.get(0).equals(spiltRuleList.get(0)) ||
                                Integer.valueOf(serialNumberList.get(0).length()).compareTo(ruleLengthList.get(0)) != 0) {
                            throw new ImppBusiException();
                        }
                        snIndex += ruleLengthList.get(0);
                        serialNumberList.remove(0);
                        ruleLengthList.remove(0);
                        spiltRuleList.remove(0);
                        break;
                    case MesPcnExtConstWords.DYNAMICRULE:
                        if (CollectionUtils.isEmpty(dynamicRuleList) || !serialNumberList.get(0).equals(dynamicRuleList.get(0)) ||
                                Integer.valueOf(serialNumberList.get(0).length()).compareTo(ruleLengthList.get(0)) != 0) {
                            throw new ImppBusiException();
                        }
                        snIndex += ruleLengthList.get(0);
                        serialNumberList.remove(0);
                        ruleLengthList.remove(0);
                        dynamicRuleList.remove(0);
                        break;
                    default:
                        throw new ImppBusiException();
                }
            }

        } catch (ImppBusiException e) {
            return resultMap;
        }

        resultMap.put(MesPcnExtConstWords.RESULT, true);
        return resultMap;
    }

    /**
     * 动态字符包含 物料识别码,条码末尾数
     * 编码规则可增加 年 月 日 时 分 秒 前缀 流水号 配置
     */
    private Map<String, Object> method1(Map<String, Object> map, MesNumberRuleExt numberRuleDb){
        MesPartExt partExt = (MesPartExt) map.get(MesPcnExtConstWords.PART_EXT);

        List<String> dynamicRuleList = new ArrayList<>();
        dynamicRuleList.add(partExt.getIdentifyCode());
        dynamicRuleList.add(partExt.getSnLastCode());

        return commonCheck(map, numberRuleDb, dynamicRuleList);
    }


    /**
     * 动态字符包含 颜色代码
     * 年月日默认使用工单计划开始时间
     * 年:取年份最后一位数
     * 月份:10月使用大写字母O,11月使用大写字母N,12月使用大写字母D
     * 编码规则可增加 日 时 分 秒 前缀 流水号 配置
     */
    private Map<String, Object> method2(Map<String, Object> map, MesNumberRuleExt numberRuleDb){
        MesPartExt partExt = (MesPartExt) map.get(MesPcnExtConstWords.PART_EXT);

        List<String> dynamicRuleList = new ArrayList<>();
        dynamicRuleList.add(partExt.getColorCode());

        return commonCheck(map, numberRuleDb, dynamicRuleList);
    }

    /**
     * 动态字符包含 物料识别码
     * 年月日默认使用工单计划开始时间
     * 年:取年份最后两位数
     * 编码规则可增加 月 日 时 分 秒 前缀 流水号 配置
     */
    private Map<String, Object> method3(Map<String, Object> map, MesNumberRuleExt numberRuleDb){
        MesPartExt partExt = (MesPartExt) map.get(MesPcnExtConstWords.PART_EXT);

        List<String> dynamicRuleList = new ArrayList<>();
        dynamicRuleList.add(partExt.getIdentifyCode());

        return commonCheck(map, numberRuleDb, dynamicRuleList);
    }

    /**
     * 动态字符包含 物料识别码
     * 年月日默认使用工单计划开始时间
     * 编码规则可增加 年 月 日 时 分 秒 前缀 流水号 配置
     */
    private Map<String, Object> method4(Map<String, Object> map, MesNumberRuleExt numberRuleDb){
        return method3(map, numberRuleDb);
    }

    /**
     * 动态字符包含 颜色代码
     * 年月日默认使用工单计划开始时间
     * 年:取年份最后两位数
     * 月份:10月使用大写字母O,11月使用大写字母N,12月使用大写字母D
     * 编码规则可增加 日 时 分 秒 前缀 流水号  配置
     */
    private Map<String, Object> method5(Map<String, Object> map, MesNumberRuleExt numberRuleDb){
        return method2(map, numberRuleDb);
    }

    /**
     * 动态字符包含 物料识别码
     * 年月日默认使用工单计划开始时间
     * 年:取年份最后两位数
     * 月份:10月使用大写字母O,11月使用大写字母N,12月使用大写字母D
     * 编码规则可增加 日 时 分 秒 前缀 流水号 配置
     */
    private Map<String, Object> method6(Map<String, Object> map, MesNumberRuleExt numberRuleDb){
        return method3(map, numberRuleDb);
    }

    /**
     * 编码规则可增加 年 月 日 时 分 秒 前缀 流水号 物料号 供应商定制代码 固定拼接字符 配置
     */
    private Map<String, Object> epmethod1(Map<String, Object> map, MesNumberRuleExt numberRuleDb){
        return commonCheck(map, numberRuleDb, null);
    }

    /**
     * 编码规则可增加 年 月 日 时 分 秒 前缀 流水号 物料号 供应商定制代码 固定拼接字符 配置
     */
    private Map<String, Object> jxmethod1(Map<String, Object> map, MesNumberRuleExt numberRuleDb){
        return commonCheck(map, numberRuleDb, null);
    }

}
