package com.reyun.service.impl;

import com.reyun.model.CommonEvent;
import com.reyun.model.CommonParam;
import com.reyun.model.ComplicatedParam;
import com.reyun.model.Event;
import com.reyun.model.EventAttributeMeta;
import com.reyun.model.EventMeta;
import com.reyun.model.VirtualEvent;
import com.reyun.repository.CommonEventRepository;
import com.reyun.repository.CommonParamRepository;
import com.reyun.repository.ComplicatedParamRepository;
import com.reyun.repository.EventAttributeMetaRepository;
import com.reyun.repository.EventMetaRepository;
import com.reyun.repository.EventRepository;
import com.reyun.service.ExpressionService;
import com.reyun.service.VirtualEventService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.StringTokenizer;

@Service
public class ExpressionServiceImpl implements ExpressionService {

    @Autowired
    VirtualEventService virtualEventService;
    @Autowired
    private EventMetaRepository eventMetaRepository;
    @Autowired
    private EventAttributeMetaRepository eventAttributeMetaRepository;
    @Autowired
    private EventRepository eventRepository;
    @Autowired
    private ComplicatedParamRepository complicatedParamRepository;
    @Autowired
    private CommonEventRepository commonEventRepository;
    @Autowired
    private CommonParamRepository commonParamRepository;

    @Override
    public boolean validateOriginalExpression(String expression, String appKey) {

//        List<String> splitExp = this.splitExpression(expression);
        List<String> splitExp = this.split(expression);

        boolean isValidExpression = this.validateExpression(splitExp);
        if (!isValidExpression) {
            System.out.println("表达式成分有问题");
            return false;
        }

        boolean bracketsValid = this.validateBrackets(splitExp);

        if (!bracketsValid) {
            System.out.println("括号验证没过");
            return false;
        }

        String eventAndAttributeValid = this.validateEventAndAttribute(splitExp, appKey);

        if (eventAndAttributeValid == null) {
            System.out.println("事件数据库验证未通过");
            return false;
        }
        return true;
    }

    /**
     * 验证事件和属性
     *
     * @param splitExp
     * @return
     */
    private String validateEventAndAttribute(List<String> splitExp, String appKey) {

        StringBuilder sb = new StringBuilder(splitExp.size());
        String eventName = "";
        String attributeName = "";
        String viewName = "";
        for (int i = 0; i < splitExp.size(); i++) {
            String str = splitExp.get(i);

            // 获取到的字符串为
            if (str.equals("(") || str.equals(")") || str.equals("+") || str.equals("-") || str.equals("*") || str.equals("/")) {
                sb.append(str);
                continue;
            }

            // 把事件  属性  操作 拆分
            StringTokenizer stringTokenizer = new StringTokenizer(str, ".");
            if (stringTokenizer.countTokens() > 2) {
                eventName = stringTokenizer.nextElement().toString();
                attributeName = stringTokenizer.nextElement().toString();
                viewName = stringTokenizer.nextElement().toString();
            } else {
                eventName = stringTokenizer.nextElement().toString();
                // 解析表达式的时候, 第二个都没有
                if (!stringTokenizer.hasMoreElements()) {
                    return null;
                }
                viewName = stringTokenizer.nextElement().toString();
                if ("".equals(viewName)) {
                    return null;
                }
            }

            // 去掉两端双引号
            if (eventName.startsWith("\"") && eventName.endsWith("\"")) {
                eventName = eventName.substring(1, eventName.length() - 1);
            }
            if (attributeName.startsWith("\"") && attributeName.endsWith("\"")) {
                attributeName = attributeName.substring(1, attributeName.length() - 1);
            }
            if (viewName.startsWith("\"") && viewName.endsWith("\"")) {
                viewName = viewName.substring(1, viewName.length() - 1);
            }
            String b = this.validateEAndA(eventName, attributeName, viewName, appKey);
            if (b == null) {
                return null;
            }
            sb.append(b);
        }

        return sb.toString();
    }

    /**
     * 到数据库去验证
     *
     * @param eventName
     * @param attributeName
     * @param viewName
     * @param appKey
     * @return
     */
    private String validateEAndA(String eventName, String attributeName, String viewName, String appKey) {

        StringBuilder sb = new StringBuilder(5);

        // 数据库读取event
        String eventId = this.getEventId(eventName, appKey);
        if (eventId == null || eventId.isEmpty()) {
            System.out.println("事件校验有误");
            return null;
        }

        sb.append(eventId).append(".");

        // 如果attribute传过来了, 数据库验证, 并验证和event的关系
        if (attributeName != null && !attributeName.isEmpty()) {
            String attrId = this.getAttributeId(eventId, attributeName, appKey);
            if (attrId == null || attrId.isEmpty()) {
                System.out.println("属性校验有误");
                return null;
            }
            sb.append(attrId).append(".");
        }

        // 验证operation
        if (viewName != null && !"".equals(viewName)) {
            ComplicatedParam complicatedParam = this.complicatedParamRepository.findComplicateOperator(viewName);
            if (complicatedParam == null) {
                System.out.println(viewName + "没找到");
                return null;
            } else {
                sb.append(complicatedParam.getViewAttr());
            }
        }

        return sb.toString();
    }

    /**
     * 验证括号匹配
     *
     * @param splitExp
     * @return
     */
    private boolean validateBrackets(List<String> splitExp) {
        Stack<String> stack = new Stack<>();
        // 配对验证
        for (String str : splitExp) {
            if (str.equals("(")) {
                stack.push("(");
//            } else if (str.equals(")") && stack.size() > 0 && stack.peek().equals("(")) {
//                stack.pop();
//            } else if (str.equals(")") && stack.size() > 0 && !stack.peek().equals("(")) {
//                stack.push(str);
            } else if (str.equals(")")) {
                if (stack.size() > 0 && stack.peek().equals("(")) {
                    stack.pop();
                } else if (stack.size() > 0 && !stack.peek().equals("(")) {
                    stack.push(str);
                } else {
                    stack.push(str);
                }
            }
        }
        if (stack.size() != 0) {
            return false;
        }
        // 括号位置验证
        for (int i = 0; i < splitExp.size(); i++) {
            String str = splitExp.get(i);
            String next = i < splitExp.size() - 1 ? splitExp.get(i + 1) : "";
            String last = i > 0 ? splitExp.get(i - 1) : "";
            // 左括号的时候, 上一个不能是dot, 下一个不能是运算符号
            if ("(".equals(str) && (last.equals(".") || next.equals("+") || next.equals("-") || next.equals("*") || next.equals("/"))) {
                return false;
            } else if (")".equals(str) && (last.equals("+") || last.equals("-") || last.equals("*") || last.equals("/") || last.equals("."))) {
                return false;
            }
        }
        // 括号一对的内容验证
//        for (int i = 0; i < splitExp.size(); i++) {
//
//        }
        return true;
    }


    private List<String> split(String expression) {
        List<String> list = new ArrayList<>(40);

        char[] chars = expression.toCharArray();
        // 存放单个属性
        StringBuilder sb = new StringBuilder();
        // 存放整个表达式
        StringBuilder sb2 = new StringBuilder();
        for (int i = 0; i < chars.length; i++) {
            char c = chars[i];
            // 如果是符号, 四则运算/小括号/dot
            if (c == '(' || c == ')' || c == '+' || c == '-' || c == '*' || c == '/') {
                // 要判断运算符号是不是名称里面的

                // 塞进去的字符串是以"开头的, 说明符号是名称里的
                if (sb.length() > 0 && sb.indexOf("\"") == 0) {
                    sb.append(c);
                }
                // 塞进去的字符串不是以"开头的, 不是以"开头, 说明这个符号不是名称里面的
                else if (sb.length() > 0 && sb.indexOf("\"") != 0) {
                    list.add(sb2.append(sb.toString()).toString());
//                    list.add(sb.toString());
                    sb.setLength(0);
                    sb2.setLength(0);
                    list.add(Character.toString(c));
                }
                // 还没塞字符串 === 这个符号不是名称里的
                else {
                    list.add(Character.toString(c));
                }
            }
            // 如果是双引号, 并且sb中存放了已开始的字符串
            else if (c == '"' && sb.length() > 0) {
                // 判断这个双引号是不是在名字中的. 判断逻辑为, 是否为字符串最后一个字符, 或下一个字符等于运算符号, 或下一个字符等于dot
//                if (i == chars.length - 1 || chars[i + 1] == '(' || chars[i + 1] == ')' || chars[i + 1] == '+' || chars[i + 1] == '-' || chars[i + 1] == '*' || chars[i + 1] == '/' || chars[i + 1] == '.') {
//                    sb.append(c);
//                    list.add(sb.toString());
//                    sb.setLength(0);
//                } else {
//                    sb.append(c);
//                }
                // 如果是最后一个字符
                if (i == chars.length - 1) {
                    sb.append(c);
                    list.add(sb.toString());
                    sb.setLength(0);
                } else {
                    sb.append(c);
                }
            }
            // 如果是双引号, 并且sb中没有字符串
            else if (c == '"' && sb.length() == 0) {
                sb.append(c);
            }
//            // 如果是dot符号
            else if (c == '.') {
                if (sb.length() > 0) {
                    sb2.append(sb.toString());
                    sb.setLength(0);
                }
                sb2.append(c);
            }
            // 最后一个字符, 收尾
            else if (i == chars.length - 1) {
                sb.append(c);
                list.add(sb2.append(sb.toString()).toString());
                sb.setLength(0);
                sb2.setLength(0);
            }
            // 其他
            else {
                sb.append(c);
            }
        }
        if (sb2.length() > 0) {
            list.add(sb2.toString());
        }
        return list;
    }

    @Override
    public String originalExpressionToMidExp(String original, String appKey) {

        List<String> splitExp = this.split(original);
        if (splitExp.size() < 1) {
            return null;
        }

        boolean isValidExpression = this.validateExpression(original);

        if (!isValidExpression) {
            System.out.println("表达式成分有问题");
            return null;
        }

        boolean bracketsValid = this.validateBrackets(splitExp);

        if (!bracketsValid) {
            System.out.println("括号验证有问题");
            return null;
        }

        String eventAndAttributeValid = this.validateEventAndAttribute(splitExp, appKey);

        if (eventAndAttributeValid == null) {
            System.out.println("事件数据库验证未通过");
            return null;
        }

        return eventAndAttributeValid;
    }

    private boolean validateExpression(List<String> splitExp) {
        if (splitExp.size() == 0) {
            return false;
        }
        // 获取表达式数量和符号数量
        int symbolCount = 0;
        int expCount = 0;
        for (String str : splitExp) {
            if ("+".equals(str) || "-".equals(str) || "*".equals(str) || "/".equals(str)) {
                symbolCount++;
            } else if ("(".equals(str) || ")".equals(str)) {
                continue;
            } else {
                expCount++;
                if (str.startsWith(".") || str.endsWith(".")) {
                    return false;
                }
            }
        }

        // 没有表达式
        if (expCount == 0) {
            return false;
        }

        // 判断开头
        String str = splitExp.get(0);
        if (str.equals("+") || str.equals("-") || str.equals("*") || str.equals("/") || str.equals(")") || str.equals(".")) {
            return false;
        }
        // 判断结尾
        str = splitExp.get(splitExp.size() - 1);
        if (str.equals("+") || str.equals("-") || str.equals("*") || str.equals("/") || str.equals("(") || str.equals(".")) {
            return false;
        }

//        return symbolCount >= 1 && expCount >= 2;
        return true;
    }

    /**
     * 获取eventId的逻辑方法
     *
     * @param eventName
     * @param appKey
     * @return
     */
    private String getEventId(String eventName, String appKey) {
        if (eventName == null || eventName.isEmpty() || appKey == null || appKey.isEmpty()) {
            return null;
        }

        //根据事件ID和别名
        EventMeta eventMeta = this.eventMetaRepository.findByAliasOrName(appKey, eventName);
        if (eventMeta != null) {
            return eventMeta.getEventId();
        }

        //根据通用事件
        CommonEvent byAlias = this.commonEventRepository.findByAlias(eventName);
        if (byAlias != null) {
            return byAlias.getEvent();
        }

        //根据事件ID
        List<Event> byEvent = eventRepository.findByEventName(appKey, eventName);
        if (byEvent != null && byEvent.size() > 0) {
            return eventName;
        }

        //虚拟事件
        VirtualEvent virtualEvent = virtualEventService.getVirtualEvent(appKey, eventName);
        if (null != virtualEvent) {
            return virtualEvent.getName();
        }

        return null;
    }

    /**
     * 获取attr Id的方法
     *
     * @param attributeName
     * @param appKey
     * @return
     */
    private String getAttributeId(String eventId, String attributeName, String appKey) {

        if (attributeName == null || attributeName.isEmpty() || appKey == null || appKey.isEmpty()) {
            return null;
        }

        // 查询别名库表
        EventAttributeMeta eventAttributeMeta = this.eventAttributeMetaRepository.findByAliasOrName(appKey, attributeName);
        if (eventAttributeMeta != null) {
            return eventAttributeMeta.getAttribute();
        }

        //查询通用属性库表
        CommonParam byAlias = this.commonParamRepository.findByAlias(attributeName, "event");
        if (byAlias != null) {
            return byAlias.getParam();
        }

        //查询属性ID
        if (eventId.startsWith("vir_")) {

            //虚拟事件的属性查询
            List<String> virtualEventList = virtualEventService.findVirtualEventList(eventId);
            if (CollectionUtils.isEmpty(eventRepository.findByEventsAndAttr(virtualEventList, attributeName, appKey))) {
                return attributeName;
            }

        } else {

            //自然事件
            List<Event> byAttr = eventRepository.findByAttr(attributeName, eventId, appKey);
            if (byAttr != null && byAttr.size() > 0) {
                return attributeName;
            }
        }

        System.out.println(attributeName + "事件属性BUG");
        return null;
    }


    /**
     * 校验算数表达式
     * created by sunhao 20170925
     */
    private boolean validateExpression(String expression) {

        //判空处理,空括号处理
        if (StringUtils.isEmpty(expression)) {
            return false;
        }

        //1.分割以及括号处理
        List<String> expressionList = this.splitWithBracket(expression);


        //2,再次校验表达式的符号
        if (!CollectionUtils.isEmpty(expressionList)) {

            //2.1,元素个数校验,偶数个false
            if (expressionList.size() % 2 == 0) {
                return false;
            }

            //2.2,校验每个元素
            for (int i = 0; i < expressionList.size(); i++) {

                String element = expressionList.get(i);

                if (element.matches("[-+*/]")) {
                    //符号处理
                    if (i - 1 < 0 || i + 1 >= expressionList.size() || expressionList.get(i - 1).matches("[-+*/]")
                            || expressionList.get(i + 1).matches("[-+*/]")) {
                        return false;
                    }

                } else if (element.startsWith("(") && element.endsWith(")")) {
                    //如果是带括号的,递归处理
                    if (!validateExpression(element.substring(1, element.length() - 1))) {
                        return false;
                    }

                } else {
                    //正常元素处理,前后若有数据,必须是符号。
                    if ((i - 1 > 0 && !expressionList.get(i - 1).matches("[-+*/]"))
                            || (i + 1 < expressionList.size() && !expressionList.get(i + 1).matches("[-+*/]"))
                            || (element.contains("(") && element.contains(")"))) {
                        return false;
                    }
                }
            }

            return true;
        }

        return false;
    }

    /**
     * 按照算数运算元素分割,顺带校验括号,括号不合法时返回NULL
     * created by sunhao 20170925
     */
    private List<String> splitWithBracket(String expression){

        List<String> expressionList = new ArrayList<>();

        String[] splitExpression = expression.split("(?<=[-+*/()])|(?=[-+*/()])");

        //1.首先校验括号,顺便聚合括号内容。
        int bracketNumber = 0;
        StringBuilder bracketElement = new StringBuilder();

        for (String element : splitExpression) {

            //『(』开头,或者括号数不是0,进入括号统计
            if (element.equals("(") || bracketNumber > 0) {

                bracketElement.append(element);

                if (element.equals("(")) {
                    bracketNumber++;
                } else if (element.equals(")")) {
                    bracketNumber--;
                }

                //括号完全匹配之后
                if (bracketNumber == 0) {

                    expressionList.add(bracketElement.toString());

                    //清空括号内容
                    bracketElement.delete(0, bracketElement.length());
                }

            } else {
                expressionList.add(element);
            }
        }

        //括号不匹配,
        if(bracketNumber != 0){
            return null;
        }

        return expressionList;
    }




    public static void main(String[] args) throws Exception {

        ExpressionServiceImpl expressionService = new ExpressionServiceImpl();

        System.out.println(expressionService.validateExpression("(付费.设备数-(注册.设备均次数))/付费.设备数-注册.设备均次数*付费.设备数"));

    }

}
