package com.reyun.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.reyun.dic.CustomMenuType;
import com.reyun.dic.ReportEnumType;
import com.reyun.dic.RoleEnumType;
import com.reyun.model.*;
import com.reyun.repository.*;
import com.reyun.service.*;
import com.reyun.taskexecute.EventReportCallable;
import com.reyun.util.*;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * Created by sunhao on 17/9/21.
 * 复杂事件service implements
 */
@Service
public class ComplicatedEventsServiceImpl implements ComplicateEventsService{

    private Logger logger = LoggerFactory.getLogger(ComplicatedEventsServiceImpl.class);

    @Autowired
    ExpressionService expressionService;

    @Autowired
    VirtualEventService virtualEventService;

    @Autowired
    EventService eventService;

    @Autowired
    ConfigParamService configParamService;

    @Autowired
    EventStatsRepository eventStatsRepository;

    @Autowired
    AppRepository appRepository;

    @Autowired
    CustomMenuTemplateRepository customMenuTemplateRepository;

    @Autowired
    ComplicatedParamRepository complicatedParamRepository;

    @Autowired
    AccountRepository accountRepository;

    @Autowired
    AuthService authService;

    @Autowired
    ReportService reportService;

    //开始时间
    private final static String SQL_START_DATE = "$startDate";
    //结束时间
    private final static String SQL_END_DATE = "$endDate";

    private final static String SQL_PARTICLE_DATE = "$selectDate";

    //显示粒度
    private final static String VIEW_PARTICLE_DAY = "day";
    private final static String VIEW_PARTICLE_WEEK = "week";
    private final static String VIEW_PARTICLE_MONTH = "month";

    private final static String FIELD_CID = "_cid";
    private final static String FIELD_CAMPAIGN_ID = "_campaignid";
    private final static String FIELD_DS = "ds";


    private final static String TEMPLATE_TYPE_EVENT = "eventstats";

    /**
     * 检查复杂事件名字
     */
    @Override
    public boolean checkEventStatsName(Long accountId, String eventStatsName, Long appId) {

        EventStats eventStats = eventStatsRepository.findEventStatsByName(eventStatsName, appId);

        return eventStats == null;
    }

    /**
     * 创建复杂事件模板
     */
    @Override
    public EventStats createEventStats(EventStats eventStats) {
        //appKey
        String appKey = appRepository.findAppkeyById(eventStats.getApp());

        //校验表达式内容正确性
        boolean checkExpression = validEventConditionJson(eventStats.getEventCondition(), appKey);

        //存储
        if (checkExpression) {
            //设置复杂事件表示
            eventStats.setComplicatedEvents(true);
            return eventStatsRepository.save(eventStats);
        } else {
            return null;
        }
    }

    /**
     * 修改复杂事件
     */
    @Override
    public int updateEventStats(EventStats eventStats, Long accountId) {

        EventStats findEventStats = eventStatsRepository.findEventStats(eventStats.getId());

        //生成SQL
        String appKey = appRepository.findAppkeyById(findEventStats.getApp());

        //校验表达式
        boolean checkExpression = validEventConditionJson(eventStats.getEventCondition(), appKey);

        String name=eventStats.getName()==null?findEventStats.getName():eventStats.getName();

        if (checkExpression) {
            List<CustomMenuTemplate> templateList = customMenuTemplateRepository.findAllTemplateByOriginal(eventStats.getId(), TEMPLATE_TYPE_EVENT);
            if(ValidateUtil.isValid(templateList)){
                List<CustomMenuTemplate> list = new ArrayList<>();
                for(CustomMenuTemplate cu : templateList){
                    cu.setName(eventStats.getName());
                    list.add(cu);
                }
                customMenuTemplateRepository.save(list);
            }
            return eventStatsRepository.updateEventStats(eventStats.getEventName(), eventStats.getEventCondition(),
                    accountId, findEventStats.getId(), new Date(),name);

        } else {

            return 0;

        }
    }

    @Override
    public int deleteEventStats(Long eventStatsId, Long accountId) {

        //查看事件是否存在看单之中,如果存在则删除看单中的模板。
        List<Long> templateList = customMenuTemplateRepository.findTemplateByOriginal(eventStatsId, CustomMenuType.EVENT_STATS.getKey());
        if (!CollectionUtils.isEmpty(templateList)) {
            customMenuTemplateRepository.deleteTemplateByIds(accountId, templateList);
        }

        //删除事件
        return eventStatsRepository.deleteEventStats(accountId, eventStatsId, new Date());
    }

    /**
     * 查询所有复杂事件的模板
     */
    @Override
    public List<EventStats> findAllEventStats(Long accountId, Long appId) {
        return eventStatsRepository.findAll(appId, true);
    }

    /**
     * 复杂事件查询
     */
    @Override
    public Map<String, List> queryComplicatedEvent(Long appId, Long accountId, String startDate, String endDate, String eventCondition, String viewType) {

        Map<String, List> result = new HashMap<>();

        App app = appRepository.findOne(appId);

        Map<String, Object> sqlMap = this.generateSql(eventCondition, app, false);

        if (CollectionUtils.isEmpty(sqlMap)) {
            return result;
        }

        String querySql = sqlMap.get("sql").toString();
        JSONArray groupJsonArray = (JSONArray)sqlMap.get("group");
        List<JSONObject> selectList = (List<JSONObject>)sqlMap.get("select");

        //替换日期
        querySql = replaceQueryParticleDate(querySql, startDate, endDate, viewType);

        //demo账号处理
        String demoApps = configParamService.getConfigParamByKey("demo_appkey");
        List<String> demoAppList = Arrays.asList(demoApps.split(","));
        String url = "";
        System.out.println(demoApps);
        System.out.println(app.getAppkey());
        if (demoAppList.contains(app.getAppkey())) {
            url = Constant.demoUrl + "/api/trackingio/eventstats/complicated/" + app.getAppkey() + "/" + viewType;
            System.out.println("........................."+url+".........................");
        }


        //母账号、管理员、子账号管理员
        querySql = querySql.replace("$campaign","");
        System.out.println(1111);
        System.out.println(querySql);
        Map<String, String> conditions = buildQueryPrestoCondition(querySql, app.getAppkey());

        ExecutorService pool = Executors.newFixedThreadPool(2);
        //创建两个有返回值的任务
        EventReportCallable c1 = new EventReportCallable(conditions,selectList,groupJsonArray,
                app,accountId,startDate,endDate,eventCondition,viewType, url);
        EventReportCallable c2 = new EventReportCallable(null,null,null,
                app,accountId,startDate,endDate,eventCondition,viewType, null);
        //执行任务并获取Future对象
        Future<Map<String, List>> f1 = pool.submit(c1);
        Future<Map<String, List>> f2 = pool.submit(c2);
        try {
            Map<String, List> res1 = f1.get();
            Map<String, List> res2 = f2.get();
            result.putAll(res1);
            result.putAll(res2);
        } catch (InterruptedException e) {
            logger.error(e.getMessage(), e);
        } catch (ExecutionException e) {
            logger.error(e.getMessage(), e);
        }finally {
            //关闭线程池
            pool.shutdown();
        }
        return result;
    }

    /**
     * 合计查询
     */
    @Override
    public List<JSONObject> queryDistinctTotalData(Long appId, Long accountId, String startDate, String endDate, String eventCondition, String viewType) {

        System.out.println("start total calculate");

        List<JSONObject> result = new ArrayList<>();

        App app = appRepository.findOne(appId);

        Map<String, Object> sqlMap = this.generateSql(eventCondition, app, true);

        if (CollectionUtils.isEmpty(sqlMap)) {
            return result;
        }

        //sql
        String querySql = sqlMap.get("sql").toString();
        JSONArray groupArray = (JSONArray) sqlMap.get("group");
        List<JSONObject> selectList = (List<JSONObject>) sqlMap.get("select");

        List<String> selectFieldList = Lists.transform(selectList, new Function<JSONObject, String>() {
            @Override
            public String apply(JSONObject jsonObject) {
                return jsonObject.getString("field");
            }
        });



        //数据权限过滤
        Account account = accountRepository.findOne(accountId);
        querySql = querySql.replace("$campaign","");
        //替换日期
        querySql = replaceQueryParticleDate(querySql, startDate, endDate, viewType);

        Map<String, String> conditions = buildQueryPrestoCondition(querySql, app.getAppkey());



        String demoApps = configParamService.getConfigParamByKey("demo_appkey");
        List<String> demoAppList = Arrays.asList(demoApps.split(","));


        Map<String, List> responseJson = new HashMap<>();
        if (demoAppList.contains(app.getAppkey())) {
            System.out.println("start to get demo data");
            String url = Constant.demoUrl + "/api/trackingio/eventstats/complicated/total/" + app.getAppkey();

            System.out.println(url);
            try {
                String responseStr = HttpClientUtil.doHttpPostRequest(url, "trackingio", conditions);
                ObjectMapper mapper = new ObjectMapper();
                responseJson = mapper.readValue(responseStr, Map.class);
            } catch (Exception e) {
                logger.error("fail to get demo data......");
            }
            System.out.println("end to get demo data");
        } else {
            responseJson = reportService.reportBySql(conditions);
        }
        //格式化汇总数据
        return formatTotalValueDate(appId, accountId, responseJson, groupArray, selectFieldList);
    }

    /**
     * 格式化汇总数据
     *
     */
    private List<JSONObject> formatTotalValueDate(Long appId, Long accountId, Map<String, List> responseJson, JSONArray groupArray, List<String> selectFieldList) {

        System.out.println("start tp format total value");

        List<JSONObject> result = new ArrayList<>();

        //处理数据
        JSONObject responseObject = JSONObject.fromObject(responseJson);
        if (!responseObject.containsKey("val")) {
            return result;
        }
        JSONArray responseValueArray = responseObject.getJSONArray("val");


        if (!CollectionUtils.isEmpty(responseValueArray)) {

            //cid,campaignid数据转换
            Map<String, String> campaignMap = new HashMap<>();
            Map<String, String> channelMap = new HashMap<>();

            //group by _cid or group by _campaignid
            if (groupArray.toString().contains(FIELD_CID) || groupArray.toString().contains(FIELD_CAMPAIGN_ID)) {
                Account account = accountRepository.findOne(accountId);
                App app = appRepository.findOne(appId);
                try {
                    String url = Constant.trackingUrl + "/api/"+ app.getId() +"/channel/findchannelcamp4io?appkey=" + app.getAppkey() + "&email=" + account.getEmail();
                    String response = HttpClientUtil.doHttpGetRequest(url, "io");
                    if (!StringUtil.isEmpty(response)) {

                            JSONObject ob = JSONObject.fromObject(response);
                            JSONObject content = ob.getJSONObject("content");
                            if (groupArray.toString().contains(FIELD_CID)) {
                                JSONObject channelObject = content.getJSONObject("channel");
                                ObjectMapper mapper = new ObjectMapper();
                                channelMap = mapper.readValue(channelObject.toString(), Map.class);
                            }
                            if (groupArray.toString().contains(FIELD_CAMPAIGN_ID)) {
                                JSONObject campObject = content.getJSONObject("campaign");
                                ObjectMapper mapper = new ObjectMapper();
                                campaignMap = mapper.readValue(campObject.toString(), Map.class);
                            }



                    }
                } catch (Exception e) {
                    logger.error("fail to get channel or campaign name");
                }

            }

            //翻译处理
            for (Object valElement : responseValueArray) {

                JSONObject val = (JSONObject) valElement;
                val.put("ds", "合计");

                //翻译推广活动或者渠道
                if (val.containsKey(FIELD_CID)) {
                    val.put(FIELD_CID, channelMap.containsKey(val.getString(FIELD_CID)) ? channelMap.get(val.getString(FIELD_CID)) : val.getString(FIELD_CID));
                }

                if (val.containsKey(FIELD_CAMPAIGN_ID)) {
                    val.put(FIELD_CAMPAIGN_ID, campaignMap.containsKey(val.getString(FIELD_CAMPAIGN_ID)) ? campaignMap.get(val.getString(FIELD_CAMPAIGN_ID)) : val.getString(FIELD_CAMPAIGN_ID));
                }

                result.add(val);
            }

            result = calculateTotalPercent(result, selectFieldList);
        }
        System.out.println("end to calculate total value");

        return result;
    }

    /**
     * 计算合计的每项的占比
     * created by sunhao 20170916
     */
    private List<JSONObject> calculateTotalPercent(List<JSONObject> totalValueList, List<String> selectFieldList) {

        JSONObject totalObject = new JSONObject();

        //统计各个指标的总和
        for (JSONObject jsonObject : totalValueList) {

            for (String field : selectFieldList) {

                if (totalObject.containsKey(field)) {

                    Object value = jsonObject.get(field);

                    //根据指标的类型累加
                    if (value instanceof Double) {
                        totalObject.put(field, totalObject.getDouble(field) + jsonObject.getDouble(field));
                    } else if (value instanceof Long) {
                        totalObject.put(field, totalObject.getLong(field) + jsonObject.getLong(field));
                    } else if (value instanceof Integer) {
                        totalObject.put(field, totalObject.getInt(field) + jsonObject.getInt(field));
                    }

                } else {

                    totalObject.put(field, jsonObject.get(field));
                }
            }
        }

        /**
         * 计算百分比
         */
        for (JSONObject jsonObject : totalValueList) {

            for (String field : selectFieldList) {

                jsonObject.put("rate_" + field, 0D == Double.valueOf(String.valueOf(totalObject.getString(field))) ? 0D : Double.valueOf(String.valueOf(jsonObject.getString(field))) / Double.valueOf(String.valueOf(totalObject.getString(field))) * 100);
            }
        }

        return totalValueList;
    }

    /**
     * 验证表达式正确性
     * created by sunhao
     */
    private boolean validEventConditionJson(String eventCondition, String appKey) {

        //校验表达式内容正确性
        boolean checkExpression = true;

        JSONObject jsonObject = JSONObject.fromObject(eventCondition);

        if (jsonObject.containsKey("expressions")) {

            JSONArray expressionArray = jsonObject.getJSONArray("expressions");

            for (Object obj : expressionArray) {

                JSONObject expressionObject = (JSONObject) obj;

                //只校验输入式表达式
                if (expressionObject.containsKey("expression")) {

                    if (!expressionService.validateOriginalExpression(expressionObject.getString("expressionName"), appKey)) {
                        checkExpression = false;
                        break;
                    }
                }
            }

        } else {
            checkExpression = false;
        }

        return checkExpression;
    }

    /**
     * 表达式转换成SQL
     * created by sunhao 20170913
     */
    private Map<String, Object> generateSql(String expressions, App app, boolean isTotal) {

        Map<String, Object> resultMap = new HashMap<>();

        if (StringUtil.isEmpty(expressions)) {
            return null;
        }

        JSONObject expressionsObject = JSONObject.fromObject(expressions);

        JSONArray eventConditionArray = expressionsObject.getJSONArray("expressions");
        JSONArray groupConditionArray = expressionsObject.getJSONArray("group");
        JSONArray paramsArray = expressionsObject.getJSONArray("params");

        //表达式转换
        List<String> selectFieldList = new ArrayList<>();
        List<JSONObject> selectFieldJson = new ArrayList<>();

        if (!CollectionUtils.isEmpty(eventConditionArray)) {

            Map<String, ComplicatedParam> operateMap = getComplicateOperatorMap();

            for (Object object : eventConditionArray) {

                JSONObject jsonObject = (JSONObject) object;

                //特殊处理字段,前面加下划线,避免字段名字是数字导致前端问题
                String fieldName = "_" + jsonObject.getString("name");

                JSONObject selectObject = new JSONObject();
                selectObject.put("field", fieldName.toLowerCase());
                selectObject.put("format", jsonObject.containsKey("format") ? jsonObject.getString("format") : "");
                selectFieldJson.add(selectObject);

                if (jsonObject.containsKey("expression")) {

                    //输入表达式模式
                    selectFieldList.add(this.translateExpression(fieldName, jsonObject.getString("expression"), operateMap));

                } else {
                    //选择表达式模式
                    selectFieldList.add(this.translateSelectExpression(fieldName, jsonObject, operateMap));
                }
            }
        }

        //筛选条件处理
        String where = " where $campaign a.ds >= '$startDate' and a.ds <= '$endDate'  and a._cid !=0 ";
        StringBuilder whereSql = new StringBuilder();
        if (!CollectionUtils.isEmpty(paramsArray)) {

            String relation = expressionsObject.getString("relation");

            //构建属性条件
            for (int i = 0; i < paramsArray.size(); i++) {

                JSONObject attr = paramsArray.getJSONObject(i);
                String attrName = attr.getString("attr");
                String attrType = attr.getString("type");
                String attrValue = attr.getString("value");
                String attrOperate = attr.getString("operator");

                if ("usergroup".equals(attrName)) {
                    //用户群
                    whereSql.append(i == 0 ? " " : relation).append(" a.xwho in (select objectid as xwho from ").append(Constant.usergroupTable)
                            .append(" where id = '").append(attrValue).append("' and objecttype = 'xwho') ");

                } else {
                    //属性
                    whereSql.append(String.format("%s %s", i == 0 ? " " : relation, StringUtil.getSql(attrName, attrValue, attrOperate, attrType, "a.")));
                }
            }

            //拼接where
            where = where + " and (" + whereSql + ")";
        }

        //group by 填充
        String groupBy = " group by $selectDate ";
        //存储用于sql的group字段,格式:",a._country,a._province,a._cid"
        String selectGroupField = "";
        //存储group的字段名,格式:"_county,_province,_cid"
        JSONArray groupJsonArray = new JSONArray();

        boolean isNeedJoinProfile = false;
        if (!CollectionUtils.isEmpty(groupConditionArray)) {

            //用户属性JOIN处理
            for (Object group : groupConditionArray) {

                JSONObject groupObject = (JSONObject) group;

                if ("profile".equals(groupObject.getString("type"))) {
                    isNeedJoinProfile = true;
                    selectGroupField += ","+" b." + groupObject.getString("name");
                } else {
                    selectGroupField += ","+" a." + groupObject.getString("name");
                }

                groupJsonArray.add(groupObject);
            }

            groupBy += selectGroupField;
        }

        //profile join
        String profileJoinSql = "";
        if (isNeedJoinProfile) {
            profileJoinSql = " right join " + Constant.profileTable + app.getAppkey() + " b on a.xwho=b.xwho ";
        }

        //拼接sql
        String selectFieldSql = String.join(",", selectFieldList);

        if (!StringUtils.isEmpty(selectFieldSql)) {

            //普通查询
            String sql = "select $selectDate as ds ," + selectFieldSql + selectGroupField + " from " + Constant.eventTable + app.getAppkey() + " a "
                    + profileJoinSql + where + groupBy + " order by ds";

            //total 查询特殊处理
            if (isTotal) {
                sql = "select " + selectFieldSql + selectGroupField + " from " + Constant.eventTable + app.getAppkey()
                        + " a " + profileJoinSql + where + (StringUtils.isEmpty(selectGroupField) ? "" : " group by " + selectGroupField.substring(1, selectGroupField.length()));
            }

            resultMap.put("sql", sql);
            resultMap.put("group", groupJsonArray);
            resultMap.put("select",selectFieldJson);

            return resultMap;

        } else {

            return null;
        }
    }

    /**
     * 获取默认显示属性
     */
    private Map<String, ComplicatedParam> getComplicateOperatorMap() {

        List<ComplicatedParam> eventViewAttrList = complicatedParamRepository.findAllParam();

        return Maps.uniqueIndex(eventViewAttrList, new Function<ComplicatedParam, String>() {
            @Override
            public String apply(ComplicatedParam complicatedParam) {
                return complicatedParam.getViewAttr();
            }
        });
    }

    /**
     * 替换查询粒度
     * created by sunhao
     */
    private String replaceQueryParticleDate(String querySql, String startDate, String endDate, String viewFlag) {

        try {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");

            Date start = simpleDateFormat.parse(startDate);
            Date end = simpleDateFormat.parse(endDate);

            switch (viewFlag) {

                case VIEW_PARTICLE_DAY:
                    querySql = querySql.replace(SQL_START_DATE, startDate);
                    querySql = querySql.replace(SQL_END_DATE, endDate);
                    querySql = querySql.replace(SQL_PARTICLE_DATE, " a.ds ");
                    break;
                case VIEW_PARTICLE_WEEK:
                    querySql = querySql.replace(SQL_START_DATE, simpleDateFormat.format(DateUtil.getFirstDayOfWeek(start)));
                    querySql = querySql.replace(SQL_END_DATE, simpleDateFormat.format(DateUtil.getLastDayOfWeek(end)));
                    querySql = querySql.replace(SQL_PARTICLE_DATE, " date_format(date_parse(a.ds, '%Y-%m-%d'), '%Y%v') ");
                    break;
                case VIEW_PARTICLE_MONTH:
                    querySql = querySql.replace(SQL_START_DATE, DateUtil.getFirstDayOfMonth(start));
                    querySql = querySql.replace(SQL_END_DATE, DateUtil.getLastDayOfMonth(end));
                    querySql = querySql.replace(SQL_PARTICLE_DATE, " date_format(date_parse(a.ds, '%Y-%m-%d'), '%Y%m') ");
                    break;
                default:
                    querySql = querySql.replace(SQL_START_DATE, startDate);
                    querySql = querySql.replace(SQL_END_DATE, endDate);
                    querySql = querySql.replace(SQL_PARTICLE_DATE, " a.ds ");
                    break;
            }

        } catch (ParseException e) {
            logger.error(e.getMessage());
        }

        return querySql;
    }

    /**
     * 根据查询粒度查询范围内的日期样式
     * created by sunhao
     */
    public Map<String, String> getDateMapByViewFlag(String startDate, String endDate, String viewFlag) {

        Map<String, String> columnDateMap = new HashMap<>();

        switch (viewFlag) {

            case VIEW_PARTICLE_DAY:
                List<String> dayList = DateUtil.getDateInterval(startDate, endDate);

                for (String date : dayList) {
                    columnDateMap.put(date, date);
                }
                break;

            case VIEW_PARTICLE_WEEK:
                List<String> weekList = DateUtil.getEveryWeek(startDate, endDate);

                for (String week : weekList) {
                    columnDateMap.put(week, DateUtil.getFirstDayOfWeek(week) + "~" + DateUtil.getLastDayOfWeek(week));
                }

                break;

            case VIEW_PARTICLE_MONTH:
                List<String> monthList = DateUtil.getEveryMonth(startDate, endDate);

                for (String month : monthList) {
                    columnDateMap.put(month, DateUtil.getFirstdayOfMonth(month) + "~" + DateUtil.getLastdayOfMonth(month));
                }
                break;

            default:
                break;
        }

        return columnDateMap;
    }

    /**
     * 转换输入形式的表达式为SQL
     * created by sunhao 20170913
     */
    private String translateExpression(String filed, String expression, Map<String, ComplicatedParam> operatorMap) {

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

        for (int i = 0; i < elements.length; i++) {

            String element = elements[i];

            if (!"-+*/()".contains(element)) {

                String[] eventArray = element.split("\\.(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)");

                String event = eventArray[0];
                String eventAttr = eventArray.length > 2 ? eventArray[1] : "";
                String operator = eventArray[eventArray.length - 1];

                //替换出sql
                elements[i] = buildFieldOperateSql(event, eventAttr, operator, operatorMap.get(operator).getAttrType());
            }
        }

        String sqlExpression = String.join("", elements);

        //除法处理,分母为0
        sqlExpression = sqlExpression.contains("/") ? dealWithDivide(sqlExpression) : sqlExpression;

        return sqlExpression + " as \"" + filed + "\"";
    }

    /**
     * 除法的递归处理
     * created by sunhao 20170916
     */
    private String dealWithDivide(String sqlExpression) {

        StringBuilder resultBuilder = new StringBuilder();

        String[] calculateElements = this.splitExpressionWithBracket(sqlExpression);

        for (int i = 0; i < calculateElements.length; i++) {


            if (i + 1 < calculateElements.length && "/".equals(calculateElements[i + 1])) {

                //分子
                String numerator = calculateElements[i];
                //分母
                String denominator = calculateElements[i + 2];

                //分子为表达式
                if ((numerator.startsWith("(") || numerator.startsWith("sum(") || numerator.startsWith("sum("))
                        && numerator.endsWith(")") && numerator.contains("/")) {

                    numerator = numerator.substring(0, numerator.indexOf("(") + 1)
                            + dealWithDivide(numerator.substring(numerator.indexOf("("), numerator.length() - 1)) + ")";
                }

                //分母为表达式
                if ((denominator.startsWith("(") || denominator.startsWith("sum(") || denominator.startsWith("sum("))
                        && denominator.endsWith(")") && denominator.contains("/")) {

                    denominator = denominator.substring(0, denominator.indexOf("(") + 1)
                            + dealWithDivide(denominator.substring(denominator.indexOf("("), denominator.length() - 1)) + ")";
                }

                resultBuilder.append(" case when ").append(denominator).append("=0 then 0 else ").append(" cast( ").append(numerator).append(" as double)").append("/").append(denominator).append(" end ");

                i += 2;

            } else {
                resultBuilder.append(calculateElements[i]);
            }
        }

        return resultBuilder.toString();
    }

    /**
     * 带括号的分割表达式
     * (a+(b+c))/c
     *  特殊样式sum(xxx)/count(xxx)需要处理
     */
    private String[] splitExpressionWithBracket(String expression){

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

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

        int bracketNumber = 0;
        StringBuilder  bracketElement = new StringBuilder();

        for (String element : elements) {

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

                bracketElement.append(element);

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

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

                    if (result.size() != 0 && (result.get(result.size() - 1).equals("sum") || result.get(result.size() - 1).equals("count"))) {
                        //括号前面是sum,count,则连在一起处理
                        String lastElement = result.get(result.size() - 1) + bracketElement.toString();
                        result.remove(result.size() - 1);
                        result.add(lastElement);

                    } else {
                        //括号前为操作符+-/*
                        result.add(bracketElement.toString());
                    }

                    bracketElement.delete(0, bracketElement.length());
                }

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

        return result.toArray(new String[result.size()]);
    }


    /**
     * 构建表达式各个元素转换后的SQL
     * created by sunhao 20170913
     */
    private String buildFieldOperateSql(String event, String eventAttr, String operate, String operateTemplate) {

        String sql = "";

        if (event.startsWith("vir_")) {

            //虚拟事件
            String virtualEvents = virtualEventService.findVirtualEvents(event);

            if (isTwoParam(operate)) {

                sql = String.format(operateTemplate, "case when  a.xwhat in (" + virtualEvents + ") then ", " else null end");

            } else if (isThreeParam(operate)) {

                //三个参数
                sql = String.format(operateTemplate, "case when a.xwhat in (" + virtualEvents + ") then a." + (StringUtil.isEmpty(eventAttr) ? "xwhat" : eventAttr) + " else " + (operate.contains("sum") ? " 0 " : " null ") + " end ",
                        "case when a.xwhat = '" + event + "' then a.", " else null end");

            } else {

                //一个参数
                sql = String.format(operateTemplate, "case when a.xwhat in (" + virtualEvents + ") then a." + (StringUtil.isEmpty(eventAttr) ? "xwhat" : eventAttr) + "  else " + (operate.equals("_sum") ? "0" : "null") + " end");
            }

        } else if (event.equals("ry_anyevent")) {

            //任意事件
            if (isTwoParam(operate)) {

                sql = String.format(operateTemplate, " ", " ");

            } else {

                sql = String.format(operateTemplate, " ");
            }

        } else {

            //具体某个事件
            if (isTwoParam(operate)) {

                //两个参数
                sql = String.format(operateTemplate, "case when a.xwhat = '" + event + "' then a.", " else null end");

            } else if (isThreeParam(operate)) {

                //三个参数
                sql = String.format(operateTemplate, "case when a.xwhat = '" + event + "' then a." + (StringUtil.isEmpty(eventAttr) ? "xwhat" : eventAttr) + " else " + (operate.contains("sum") ? " 0 " : " null ") + " end ",
                        "case when a.xwhat = '" + event + "' then a.", " else null end");

            } else {

                //一个参数
                sql = String.format(operateTemplate, "case when a.xwhat = '" + event + "' then a." + (StringUtil.isEmpty(eventAttr) ? "xwhat" : eventAttr) + "  else " + (operate.equals("_sum") ? "0" : "null") + " end");
            }
        }

        return sql;
    }

    /**
     * SQL模板有两个参数
     * created by sunhao 20170913
     */
    private boolean isTwoParam(String operator) {

        List<String> twoParamOperator = Arrays.asList("_distinct_device", "_distinct_xwho");

        return twoParamOperator.contains(operator);
    }

    /**
     * SQL模板有三个参数
     * created by sunhao 20170913
     */
    private boolean isThreeParam(String operator) {

        List<String> twoParamOperator = Arrays.asList("_xwho_distinct_count_avg", "_device_distinct_count_avg", "_xwho_distinct_sum_avg", "_device_distinct_sum_avg");

        return twoParamOperator.contains(operator);
    }

    /**
     * 转换选择框形式的表达式为SQL
     * created by sunhao 20170913
     */
    private String translateSelectExpression(String field, JSONObject selectExpression, Map<String, ComplicatedParam> operatorMap) {

        String sqlSelect = "";

        //事件,属性,字段别名,操作
        String event = selectExpression.getString("event");
        String eventAttr = selectExpression.containsKey("attr") ? selectExpression.getString("attr") : "";
        String operator = selectExpression.getString("operator");

        //sql模板
        ComplicatedParam complicatedParam = operatorMap.get(operator);

        if (null != complicatedParam) {

            sqlSelect = buildFieldOperateSql(event, eventAttr, operator, complicatedParam.getAttrType());

            sqlSelect = sqlSelect.contains("/") ? dealWithDivide(sqlSelect) : sqlSelect;

            return sqlSelect + " as \"" + field + "\"";

        } else {

            return sqlSelect;
        }

    }

    /**
     * 构建查询presto参数
     * created by sunhao
     */
    private Map<String, String> buildQueryPrestoCondition(String querySql, String appKey) {

        Map<String, String> conditions = new HashMap<>();

        conditions.put("sql", querySql);
        conditions.put("dbtype", "presto");
        conditions.put("datatype", ReportEnumType.LIST.getCode());
        conditions.put("reportname", "eventstats");
        conditions.put("appid", appKey);


        return conditions;
    }

    /**
     * 格式化返回值
     * created by sunhao
     */
    public Map<String, List> formatComplicatedEventResponse(App app, Long accountId, Map<String, List> responseJson, Map<String,
            String> dateStringMap, JSONArray groupArray, List<JSONObject> selectList) {

        Map<String, List> resultMap = new HashMap<>();

        //结果元素值
        List<JSONObject> valueResult = new ArrayList<>();
//        Map<String, JSONObject> totalValueMap = new HashMap<>();
        List<String> nameList = Lists.newArrayList();
        List<String> columnKeyList = new ArrayList<>();
        List<String> keyList = new ArrayList<>();
        List<String> groupList = new ArrayList<>();
//        List<JSONObject> totalValueList = new ArrayList<>();
        List<String> selectFieldList = new ArrayList<>();
        List<String> selectFieldListNoDash = new ArrayList<>();

        //转换select字段
        for (JSONObject object : selectList) {
            selectFieldList.add(object.getString("field"));
            selectFieldListNoDash.add(object.getString("field").substring(1, object.getString("field").length()));
        }

        try {

            JSONObject responseObject = JSONObject.fromObject(responseJson);
            if (!responseObject.containsKey("val")) {
                resultMap.put("val", valueResult);
                resultMap.put("columnkey", columnKeyList);
                resultMap.put("name", nameList);
                resultMap.put("key", keyList);
                resultMap.put("groupfield", groupList);
                resultMap.put("selectfield", selectFieldList);
                resultMap.put("format", selectList);

                return resultMap;
            }
            JSONArray responseValueArray = responseObject.getJSONArray("val");

            //val
            if (!CollectionUtils.isEmpty(responseValueArray)) {

                System.out.println("start to get channel name from tracking");

                //cid,campaignid数据转换
                Map<String, String> campaignMap = new HashMap<>();
                Map<String, String> channelMap = new HashMap<>();

                //group by _cid or group by _campaignid
                if (groupArray.toString().contains(FIELD_CID) || groupArray.toString().contains(FIELD_CAMPAIGN_ID)) {
                    Account account = accountRepository.findOne(accountId);
                    try {
                        String url = Constant.trackingUrl + "/api/"+ app.getId() +"/channel/findchannelcamp4io?appkey=" + app.getAppkey() + "&email=" + account.getEmail();
                        String response = HttpClientUtil.doHttpGetRequest(url, "io");
                        if (!StringUtil.isEmpty(response)) {

                                JSONObject ob = JSONObject.fromObject(response);
                                JSONObject content = ob.getJSONObject("content");
                                if (groupArray.toString().contains(FIELD_CID)) {
                                    JSONObject channelObject = content.getJSONObject("channel");
                                    ObjectMapper mapper = new ObjectMapper();
                                    channelMap = mapper.readValue(channelObject.toString(), Map.class);
                                }
                                if (groupArray.toString().contains(FIELD_CAMPAIGN_ID)) {
                                    JSONObject campObject = content.getJSONObject("campaign");
                                    ObjectMapper mapper = new ObjectMapper();
                                    campaignMap = mapper.readValue(campObject.toString(), Map.class);
                                }



                        }
                    } catch (Exception e) {
                        logger.error("fail to get channel or campaign name");
                    }

                }
                System.out.println("end to get channel name from tracking");

                //1,处理日期合并(给每个日期的第一行加标注,firstDateRow标示第一行,rowNum标示相同日期行数)
                String firstValueDate = "";
                JSONObject firstVal = null;
                int number = 1;

                //计算日期块内的合计
                Map<String, JSONObject> dateTotalValueMap = new HashMap<>();
                JSONObject jsonObjectTotal = new JSONObject();

                for (Object valElement : responseValueArray) {

                    JSONObject val = (JSONObject) valElement;

                    String valDate = val.getString(FIELD_DS);

                    //处理日期合并
                    if (!firstValueDate.equals(valDate)) {

                        if (null != firstVal) {
                            firstVal.put("firstDateRow", true);
                            firstVal.put("rowNum", number);
                            //key
                            keyList.add(dateStringMap.get(firstValueDate));
                            //计算日期内total
                            dateTotalValueMap.put(firstValueDate, jsonObjectTotal);
                        }

                        firstValueDate = valDate;
                        firstVal = val;
                        number = 1;
                        //日期合计
                        jsonObjectTotal = JSONObject.fromObject(valElement);

                    } else {

                        number++;
                        //累加日期内的指标值
                        jsonObjectTotal = addTwoJSONObject(selectFieldList, jsonObjectTotal, val);
                    }

                    /*//2翻译推广活动或者渠道
                    if (val.containsKey(FIELD_CID)) {
                        String cid = val.getString(FIELD_CID);
                        val.put(FIELD_CID, channelMap.containsKey(cid) ? channelMap.get(cid) : cid);
                    }

                    if (val.containsKey(FIELD_CAMPAIGN_ID)) {
                        String campaignId = val.getString(FIELD_CAMPAIGN_ID);
                        val.put(FIELD_CAMPAIGN_ID, campaignMap.containsKey(campaignId) ? campaignMap.get(campaignId) : campaignId);
                    }*/


                    //添加到val中
                    valueResult.add(val);
                }

                System.out.println("1111111111111111111111111111111111111");

                //处理最后一个日期
                if (null != firstVal) {
                    firstVal.put("firstDateRow", true);
                    firstVal.put("rowNum", number);
                    //key
                    keyList.add(dateStringMap.get(firstValueDate));
                    //计算日期内total
                    dateTotalValueMap.put(firstValueDate, jsonObjectTotal);
                }


                //计算每行指标占比
                for (JSONObject value : valueResult) {

                    if (dateTotalValueMap.containsKey(value.getString(FIELD_DS))) {

                        JSONObject totalObject = dateTotalValueMap.get(value.getString(FIELD_DS));

                        for (String field : selectFieldList) {
                            value.put("rate_" + field, 0D == Double.valueOf(String.valueOf(totalObject.getString(field))) ? 0D : Double.valueOf(String.valueOf(value.getString(field))) / Double.valueOf(String.valueOf(totalObject.getString(field))) * 100);
                        }
                    }

                    //替换日期
                    value.put(FIELD_DS, dateStringMap.get(value.getString(FIELD_DS)));
                }
                System.out.println("222222222222222222222222222222222");
            }

            //columnKey
            columnKeyList.add(FIELD_DS);

            //name
            nameList.add("日期");

            //分组列
            if (!CollectionUtils.isEmpty(groupArray)) {

                //转换group的名称
                Map<String, EventAttr4Web> commonEventProperties = getCommonPropertiesMap(app.getId(), null);
                for (Object group : groupArray) {

                    JSONObject groupObject = (JSONObject) group;
                    String groupField = groupObject.getString("name");
                    String key = groupField + "_" + groupObject.getString("type");

                    //大小写转换,sql查询之后变成小写
                    columnKeyList.add(groupField);
                    groupList.add(groupField);

                    nameList.add(commonEventProperties.containsKey(key) ? commonEventProperties.get(key).getAttrAlias() : groupField);
                }
            }
            System.out.println("3333333333333333333333333333333333333");

            //指标列
            if (!CollectionUtils.isEmpty(selectFieldList)) {
                columnKeyList.addAll(selectFieldList);
                nameList.addAll(selectFieldListNoDash);
            }

            //处理合计百分比
//            totalValueList = calculateTotalPercent(Lists.newArrayList(totalValueMap.values()), selectFieldList);


        } catch (Exception e) {
            logger.error(e.getMessage());
        }

        //设置结果
        resultMap.put("val", valueResult);
        resultMap.put("columnkey", columnKeyList);
        resultMap.put("name", nameList);
        resultMap.put("key", keyList);
        resultMap.put("groupfield", groupList);
        resultMap.put("selectfield", selectFieldList);
        resultMap.put("format", selectList);

        return resultMap;
    }

    /**
     * 获取通用属性的MAP
     * Key 为param_type组合
     */
    private Map<String, EventAttr4Web> getCommonPropertiesMap(Long appId,String propertiesType) {

        Map<String, EventAttr4Web> eventAttr4WebMap = new HashMap<>();

        List<EventAttr4Web> eventAttr4WebList = eventService.listCommonProperties(appId, propertiesType);

        eventAttr4WebMap = Maps.uniqueIndex(eventAttr4WebList, new Function<EventAttr4Web, String>() {
            @Override
            public String apply(EventAttr4Web eventAttr4Web) {
                return eventAttr4Web.getAttr() + "_" + eventAttr4Web.getType();
            }
        });

        return eventAttr4WebMap;
    }

    /**
     * 两个数累加
     */
    private JSONObject addTwoJSONObject(List<String> selectList, JSONObject result, JSONObject value) {

        for (String selectField : selectList) {
            //根据指标的类型累加
            if (result.get(selectField) instanceof Double) {
                result.put(selectField, result.getDouble(selectField) + value.getDouble(selectField));
            } else if (result.get(selectField) instanceof Long) {
                result.put(selectField, result.getLong(selectField) + value.getLong(selectField));
            } else if (result.get(selectField) instanceof Integer) {
                result.put(selectField, result.getInt(selectField) + value.getInt(selectField));
            }
        }

        return result;
    }


    /**
     * 处理历史数据
     * 临时方法
     */
    @Override
    public boolean importOldEventData(Account account) {

        List<EventStats> saveList = new ArrayList<>();

        List<EventStats> eventStatsList = eventStatsRepository.findAllEsay();

        Map<String, ComplicatedParam> operateMap = getComplicateOperatorMap();

        try {
            for (EventStats eventStats : eventStatsList) {

                if (!StringUtil.isEmpty(eventStats.getEventCondition())) {

                    //新建一个
//                    EventStats newEventStats = new EventStats();
//
//                    newEventStats.setApp(eventStats.getApp());
//                    newEventStats.setCreateAccount(eventStats.getCreateAccount());
//                    newEventStats.setEventAlias(eventStats.getEventAlias());
//                    newEventStats.setEventName(eventStats.getEventName());
//                    newEventStats.setCreateTime(eventStats.getCreateTime());
//                    newEventStats.setModifyAccount(eventStats.getModifyAccount());
//                    newEventStats.setName("历史_"+eventStats.getName());
//                    newEventStats.setQuerySql(eventStats.getQuerySql());
//                    newEventStats.setValid(eventStats.getValid());

                    JSONObject oldObject = JSONObject.fromObject(eventStats.getEventCondition());

                    if (null ==eventStats.getComplicatedEvents() || (!eventStats.getComplicatedEvents() && oldObject.containsKey("event"))) {

                        JSONObject newObject = new JSONObject();

                        newObject.put("relation", oldObject.get("relation"));
                        newObject.put("params", oldObject.get("params"));

                        JSONObject expression = new JSONObject();

                        expression.put("event", oldObject.getString("event"));

                        //事件属性
                        String attr = "";
                        String operate = oldObject.getString("viewField");

                        if (oldObject.containsKey("fieldOperate") && !StringUtils.isEmpty(oldObject.getString("fieldOperate"))) {

                            attr = oldObject.getString("viewField");

                            operate = oldObject.getString("fieldOperate");
                        }

                        //操作符
                        if ("_count_user".equals(operate)) {
                            //_count_user => _distinct_xwho
                            operate = "_distinct_xwho";
                        } else if ("_count_avg".equals(operate)) {
                            //_count_avg =>_xwho_distinct_count_avg
                            operate = "_xwho_distinct_count_avg";
                        }

                        expression.put("attr", attr);
                        expression.put("operator", operate);

                        //表达式名字
                        expression.put("name", oldObject.getString("event") + attr + operateMap.get(operate).getViewAttrName());

                        JSONArray expressions = new JSONArray();
                        expressions.add(expression);

                        newObject.put("expressions", expressions);
                        newObject.put("group", new JSONArray());

                        eventStats.setEventCondition(newObject.toString());
                        eventStats.setComplicatedEvents(true);
                        saveList.add(eventStats);
                    }
                }
            }

            eventStatsRepository.save(saveList);

        } catch (Exception e){
            logger.error(e.getMessage(), e);
            //e.printStackTrace();
            return false;
        }


        return true;
    }


    @Override
    public boolean checkImportComplicated(Account account) {

        return !CollectionUtils.isEmpty(eventStatsRepository.findEventStatsByABTest(account.getRootParent()))
                && CollectionUtils.isEmpty(eventStatsRepository.findComplicatedRecord(account.getRootParent()));
    }
}
