package com.reyun.util;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.reyun.model.EventViewAttr;
import com.reyun.model.IntelligentPath;
import com.reyun.model.ReportListResult;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SqlUtil
{

    private final static String VIEW_DAY = "day";
    private final static String VIEW_WEEK = "week";
    private final static String VIEW_MONTH = "month";

    public static String generateFunnelSqlTemp(String eventinfo, String appid, int window)
    {
        JSONArray eventArray = JSONArray.fromObject(eventinfo);
        StringBuffer funnelEventsSB = new StringBuffer();
        StringBuffer paramsSB = new StringBuffer();
        List<String> events = new ArrayList<>();
        String firstEvent = "";
        String firstParam = "";
        for (int i = 0; i < eventArray.size(); i++) {
            JSONObject event = eventArray.getJSONObject(i);
            String name = event.getString("event");
            if (i == 0) {
                firstEvent = name;
            }
            else {
                events.add(name);
            }
            funnelEventsSB.append(String.format("%s%s", i == 0 ? "" : ",", name));
            if (event.containsKey("params")) {
                JSONArray paramArray = event.getJSONArray("params");
                if (paramArray.size() > 0) {
                    String relation = event.getString("relation");

                    StringBuffer param1EventSB = i == 0 ? new StringBuffer() : new StringBuffer(String.format(" and case when xwhat='%s' then ", name));
                    param1EventSB = getEventParamStringBuffer(param1EventSB, paramArray, relation);

                    if (i == 0) {
                        firstParam = param1EventSB.toString();
                    }
                    else {
                        paramsSB.append(param1EventSB).append(" else 1=1 end");
                    }
                }
            }
        }

        return String.format("select $dimentionselect ld_sum(temp, %s) "
                        + "from ( "
                        + "select $dimentionselect ld_count(xwhen, $interval, %s*86400, $startdatetime, xwhat, '%s') as temp "
                        + "from %s "
                        + "where _deviceid!='00000000-0000-0000-0000-000000000000' and ds >= '$startdate' $dimentionwhere $usergroup and "
                        + "( "
                        + "(xwhat = '%s' and ds < '$firstenddate' %s) "
                        + "or "
                        + "(xwhat in ('%s') and ds <= '$secondenddate' %s) "
                        + ") "
                        + "group by xwho $dimentionaftergroupby"
                        + ") $dimentiongroupby",
                eventArray.size(), window, funnelEventsSB, Constant.eventTable + appid, firstEvent,
                StringUtil.isEmpty(firstParam) ? "" : " and (" + firstParam + ")",
                String.join("','", events), paramsSB.length() > 0 ? paramsSB : "");
    }

    /**
     * 生成事件概览SQL
     * created by sunhao 2017-04-11
     * <p>
     * 参数模板中参数:
     * $groupFieldSql 分类维度中需要group的字段
     * $selectDate 查询时间粒度
     * $startData 开始时间
     * $endDate 结束时间
     * $whereFieldSql 分类维度中的字段值
     */
    public static String generateEventStatsSqlTemplate(String eventCondition, String appKey, Map<String, EventViewAttr> eventViewAttrMap)
    {

        final String eventTable = Constant.eventTable + appKey;
        StringBuilder sqlWhere = new StringBuilder();
        sqlWhere.append(" 1 = 1 ");

        JSONObject conditionObject = JSONObject.fromObject(eventCondition);

        String eventName = conditionObject.getString("event");
        String relation = conditionObject.getString("relation");

        /**
         * 分类维度参数:
         * viewField 显示字段
         * filedOperate 字段操作
         * {"viewField":"_payment","fieldOperate":"_min"}
         * eg:
         * 显示字段 xwho _payment
         * 总次数 count(*)
         * 总人数 count(distinct xwho )
         * 人均总数 count(*)/count(distinct xwho )
         * 总和 sum(x)
         * 均值 avg(x)
         * 最大值 max(x)
         * 最小值 min(x)
         *
         */
        String viewField = conditionObject.getString("viewField");
        String fieldOperate = conditionObject.getString("fieldOperate");

        /**
         * 选择默认显示属性,{"viewField":"_count","fieldOperate":""}
         * 选择自定义属性,  {"viewField":"_payment","fieldOperate":"_min"}
         */
        if (StringUtil.isEmpty(fieldOperate)) {
            EventViewAttr eventViewAttr = eventViewAttrMap.get(viewField);
            fieldOperate = null != eventViewAttr ? eventViewAttr.getAttrType() : null;
            viewField = "xwho";
        }
        else {
            EventViewAttr eventViewAttr = eventViewAttrMap.get(fieldOperate);
            fieldOperate = null != eventViewAttr ? eventViewAttr.getAttrType() : null;
        }

        //属性
        JSONArray paramArray = (JSONArray) conditionObject.get("params");

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

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

                sqlWhere.append(String.format("%s %s", i == 0 ? "and " : relation, StringUtil.getSql(attrName, attrValue, attrOperate, attrType, "")));
            }
        }

        //参数校验
        if (StringUtil.isEmpty(fieldOperate) || StringUtil.isEmpty(eventTable) || StringUtil.isEmpty(eventName)) {
            return null;
        }

        //聚合字段
        String operateField = String.format(fieldOperate, viewField);

        //返回结果
        return String.format("select  $selectDate as ds, %s as sumData  $selectFieldSql " +
                        " from %s " +
                        " where $campaign _deviceid != '00000000-0000-0000-0000-000000000000' and xwhat = '%s' and ds >= '$startData'  and ds <= '$endDate' and  (%s)  $whereFieldSql " +
                        " group by $selectDate $groupFieldSql order by ds desc , sumData desc",
                operateField,
                eventTable,
                eventName,
                sqlWhere.toString());
    }

    private static int getRetentions(String reportView)
    {

        int retentionInterval;

        switch (reportView) {

            case VIEW_WEEK:
                retentionInterval = 8;
                break;
            case VIEW_MONTH:
                retentionInterval = 3;
                break;
            default:
                retentionInterval = 30;
                break;
        }

        return retentionInterval;
    }

    public static String generateRetentionSql(String eventinfo, String appid, String reportView)
    {

        int retentionInterval = getRetentions(reportView);
        JSONArray eventArray = JSONArray.fromObject(eventinfo);

        StringBuffer whereSB = new StringBuffer(" where ");
        whereSB.append(String.format("(", appid));

        String events = "";

        for (int i = 0; i < eventArray.size(); i++) {

            JSONObject event = eventArray.getJSONObject(i);
            String name = event.getString("event");
            events += i == 0 ? name : "," + name;

            StringBuffer param1EventSB = new StringBuffer();

            if (event.containsKey("params")) {

                JSONArray paramArray = event.getJSONArray("params");

                if (paramArray.size() > 0) {

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

                    param1EventSB.append(" and (");
                    param1EventSB = getEventParamStringBuffer(param1EventSB, paramArray, relation);
                    param1EventSB.append(")");
                }
            }

            if (i == 0) {
                whereSB.append(String.format(" ( $campaign _deviceid!='00000000-0000-0000-0000-000000000000' and ds>='$firststartdate' and ds < '$firstenddate' and xwhat = '%s' %s)",
                        name, param1EventSB.length() > 0 ? param1EventSB : ""));
            }
            else {
                whereSB.append(String.format(" or ($campaign _deviceid!='00000000-0000-0000-0000-000000000000' and ds >= '$secondstartdate' and ds <= '$secondenddate' and xwhat = '%s' %s)",
                        name, param1EventSB.length() > 0 ? param1EventSB : ""));
            }
        }
        System.out.println("=======================================" + whereSB);
        return String.format("select $dimentionselect lc_sum(temp, $interval, %s) "
                        + "from ("
                        + "select $dimentionselect lc_count("
                        + "date_diff('%s', from_iso8601_timestamp('2007-01-01'), from_unixtime(xwhen)),"
                        + "date_diff('%s', from_iso8601_timestamp('2007-01-01'),  from_iso8601_timestamp('$firststartdate')), "
                        + "$interval, %s, xwhat, '%s') as temp "
                        + "from %s %s $dimentionwhere $usergroup "
                        + "group by xwho $dimentionaftergroupby) $dimentiongroupby",
                retentionInterval, reportView, reportView, retentionInterval, events, Constant.eventTable + appid, whereSB + ")");
    }

    public static String generateVirtualRetentionSql(String events, String appid, String reportView)
    {

        int retentionInterval = getRetentions(reportView);

        String[] singleEvent = events.split(",");
        StringBuilder sb = new StringBuilder();
        for (String s : singleEvent) {
            sb.append("'" + s + "',");
        }
        String xwhatevents = sb.deleteCharAt(sb.length() - 1).toString();

        StringBuffer whereSB = new StringBuffer(" where $campaign ds>='$firststartdate' and ds<='$secondenddate'");
        return String.format("select $dimentionselect active_lc_sum(temp, $interval, %s) "
                        + "from ("
                        + "select $dimentionselect active_lc_count("
                        + "date_diff('%s', from_iso8601_timestamp('$firststartdate'), from_iso8601_timestamp(ds)),"
                        + "$interval, %s, xwhat, '%s') as temp "
                        + "from %s %s $dimentionwhere $usergroup "
                        + "and xwhat in (%s) "
                        + "group by xwho   $dimentionaftergroupby ) $dimentiongroupby",
                retentionInterval, reportView, retentionInterval, events, Constant.eventTable + appid, whereSB, xwhatevents);
    }

    public static String generateInstallVirtualRetentionSql(String events, String appid, String reportView)
    {

        int retentionInterval = getRetentions(reportView);

        return String.format("select $firstdimentionselect added_lc_sum(temp, $interval, %s) "
                        + "from (select $dimentionselect added_lc_count(t0.xwhat,date_diff("
                        + "'%s',from_iso8601_timestamp('$firststartdate'), from_iso8601_timestamp(substr(t1._register_time,1,10))),"
                        + "date_diff('%s',from_iso8601_timestamp(substr(t1._register_time,1,10)), from_iso8601_timestamp(t0.ds)),"
                        + "$interval, %s,'%s') as temp from %s t0 right join %s t1 on t0.xwho = t1.xwho "
                        + "where $campaign substr(t1._register_time,1,10) >= '$firststartdate' and substr(t1._register_time,1,10) "
                        + "< '$firstenddate' and t0.ds >= '$secondstartdate' and t0.ds <= '$secondenddate'  $dimentionwhere  $usergroup group by t0.xwho $dimentionaftergroupby) $dimentiongroupby;",
                retentionInterval, reportView, reportView, retentionInterval, events, Constant.eventTable + appid, Constant.profileTable + appid);
    }

    private static StringBuffer getEventParamStringBuffer(StringBuffer param1EventSB, JSONArray paramArray, String relation)
    {

        for (int j = 0; j < paramArray.size(); j++) {

            JSONObject param = paramArray.getJSONObject(j);
            String attr = param.getString("attr");
            String type = param.getString("type");
            String value = param.getString("value");
            String oper = param.getString("operator");

            param1EventSB.append(String.format("%s %s", j == 0 ? "" : relation, StringUtil.getSql(attr, value, oper, type, "")));
        }

        return param1EventSB;
    }

    private static Map<String, List> format(String reponse)
    {

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

        try {
            ReportListResult content = new ObjectMapper().readValue(reponse, ReportListResult.class);
            List<Map<String, Object>> valList = content.getVal();

            if (valList.size() > 0) {
                Map<String, Object> valueMap = valList.get(0);
                List<Object> users = new ArrayList<>();

                for (String k : valueMap.keySet()) {
                    if (!"isall0".equals(k)) {
                        users.add(valueMap.get(k));
                    }
                }

                List<Object> trans4firstList = new ArrayList<>();
                List<Object> trans4lastList = new ArrayList<>();

                for (int i = 0; i < users.size(); i++) {

                    Double trans4first = Double.valueOf(users.get(i).toString()) * 100.0 / Double.valueOf(users.get(0).toString());
                    BigDecimal trans4firstb = BigDecimal.valueOf(trans4first);

                    Object trans4firstResult = trans4firstb.setScale(2, BigDecimal.ROUND_HALF_UP);
                    trans4firstList.add(users.get(0).toString().equals("0") ? 0 : trans4firstResult);
                    if (i > 0) {

                        Double trans4last = Double.valueOf(users.get(i).toString()) * 100.0 / Double.valueOf(users.get(i - 1).toString());
                        BigDecimal trans4lastb = BigDecimal.valueOf(trans4last);
                        Object trans4lastResult = trans4lastb.setScale(2, BigDecimal.ROUND_HALF_UP);

                        trans4lastList.add(users.get(i - 1).toString().equals("0") ? 0 : trans4lastResult);
                    }
                }

                valueResult.add(trans4firstList);
                valueResult.add(users);
                result.put("trans4last", trans4lastList);
            }

            result.put("val", valueResult);
        }
        catch (IOException e) {
            e.printStackTrace();
        }

        return result;
    }

    /**
     * 格式化来自报表的漏斗数据
     */
    public static Map<String, List> format4Funnel(Map<String, List> response, List<String> eventList, boolean isCompare, String dimension,
            String startDate, String endDate, boolean isTotal, String userGroup,
            String userGroupName, Map<String, String> eventAlisMap, String dimensionValue)
    {

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

        List<Map<String, Object>> valList = response.get("val");
        List<String> columnKeyList = new ArrayList<>();
        List<String> nameList = new ArrayList<>();
        List<String> alias = new ArrayList<>();

        for (String e : eventList) {
            alias.add(eventAlisMap.get(e));
        }

        if (valList != null && valList.size() > 0) {

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

            for (Map<String, Object> val : valList) {

                if (isCompare) {

                    Map<String, Object> innerMap = new HashMap<>();
                    JSONArray valArray = new JSONArray();

                    if (StringUtil.isEmpty(userGroup)) {
                        valArray = JSONArray.fromObject(val.get("_col1"));
                    }
                    else {
                        valArray = JSONArray.fromObject(val.get("_col0"));
                    }

                    if (valArray.size() > 0) {

                        if (StringUtil.isEmpty(userGroup)) {
                            dimensionValueList.add(val.get(dimension).toString());
                            innerMap.put(dimension, val.get(dimension));
                        }
                        else {
                            innerMap.put(dimension, userGroupName);
                        }

                        if (!columnKeyList.contains(dimension)) {
                            columnKeyList.add(dimension);
                            nameList.add(dimension);
                        }

                        List<Object> valSumList = valArray.subList(valArray.size() - eventList.size(), valArray.size());
                        List<Long> users = new ArrayList<>();
                        for (Object o : valSumList) {
                            users.add(Long.valueOf(o.toString()));
                        }

                        deal1Funnel(users, eventList, columnKeyList, nameList, innerMap, eventAlisMap);
                        valueResult.add(innerMap);
                    }
                }
                else if (isTotal) {

                    JSONArray valArray = JSONArray.fromObject(val.get("_col0"));

                    if (valArray.size() > 0) {
                        List<Object> usersObject = valArray.subList(valArray.size() - eventList.size(), valArray.size());

                        Map<String, Object> innerMap = new HashMap<>();
                        innerMap.put(dimension, "整体");

                        if (!columnKeyList.contains(dimension)) {
                            columnKeyList.add(dimension);
                            nameList.add(dimension);
                        }

                        List<Long> users = new ArrayList<>();
                        for (Object o : usersObject) {
                            users.add(Long.valueOf(o.toString()));
                        }

                        deal1Funnel(users, eventList, columnKeyList, nameList, innerMap, eventAlisMap);
                        valueResult.add(innerMap);
                    }
                }
                else {

                    JSONArray valArray = JSONArray.fromObject(val.get("_col0"));
                    if (valArray.size() > 0) {

                        List<String> dateList = DateUtil.getDateInterval(startDate, endDate);
                        List<Object> usersAll = Arrays.asList(valArray.toArray());

                        for (int i = 0; i <= dateList.size(); i++) {

                            Map<String, Object> innerMap = new HashMap<>();
                            innerMap.put("ds", i == dateList.size() ? "整体" : dateList.get(i));

                            if (!columnKeyList.contains("ds")) {
                                columnKeyList.add("ds");
                                nameList.add("日期");
                            }

                            List<Long> users = new ArrayList<>();
                            for (int j = 0; j < eventList.size(); j++) {
                                users.add(Long.valueOf(usersAll.get(i * eventList.size() + j).toString()));
                            }

                            deal1Funnel(users, eventList, columnKeyList, nameList, innerMap, eventAlisMap);

                            if (i == dateList.size()) {
                                valueResult.add(0, innerMap);
                            }
                            else {
                                valueResult.add(innerMap);
                            }
                        }
                    }
                }
            }
            if (!StringUtil.isEmpty(dimensionValue)) {
                String[] dimentionValueList4Web = dimensionValue.split(",");
                if (valueResult.size() < dimentionValueList4Web.length) {
                    for (String dv : dimentionValueList4Web) {
                        if (!dimensionValueList.contains(dv)) {
                            valueResult.addAll(getEmptyFunnelResult(eventList, alias, dimension, dv).get("val"));
                        }
                    }
                }
            }
        }

        result.put("val", valueResult);
        result.put("columnkey", columnKeyList);
        result.put("name", nameList);
        result.put("eventname", eventList);
        result.put("key", alias);

        if (valueResult.isEmpty()) {
            if (!StringUtil.isEmpty(dimension) && dimension.equals("usergroup")) {
                dimensionValue = userGroupName;
            }
            result = getEmptyFunnelResult(eventList, alias, dimension, dimensionValue);
            result.put("eventname", eventList);
            result.put("key", alias);
        }

        return result;
    }

    public static void deal1Funnel(List<Long> users, List<String> eventList, List<String> columnKeyList,
            List<String> nameList, Map<String, Object> innerMap, Map<String, String> eventAlisMap)
    {

        for (int i = 0; i < users.size(); i++) {

            String eventName = eventList.get(i);
            innerMap.put(eventName, users.get(i));

            Double trans4first = users.get(0).equals(0L) ? 0d : users.get(i) * 100.0 / users.get(0);
            BigDecimal trans4firstb = BigDecimal.valueOf(trans4first);
            Object trans4firstResult = trans4firstb.setScale(2, BigDecimal.ROUND_HALF_UP);

            innerMap.put("rate_" + eventName, users.get(0).equals(0L) ? i == 0 ? 100 : 0 : trans4firstResult);

            if (i > 0) {
                Long last = users.get(i - 1);
                innerMap.put(eventName + "lost", last - users.get(i));
                if (!columnKeyList.contains(eventName + "lost")) {
                    columnKeyList.add(eventName + "lost");
                    nameList.add("流失");
                }

                Double lost4last = last == 0 ? 0d : (last - users.get(i)) * 100.0 / last;
                BigDecimal lost4lastb = BigDecimal.valueOf(lost4last);
                Object lost4lastResult = lost4lastb.setScale(2, BigDecimal.ROUND_HALF_UP);
                innerMap.put("rate_" + eventName + "lost", lost4lastResult);
            }
            if (!columnKeyList.contains(eventName)) {
                columnKeyList.add(eventName);
                nameList.add(eventAlisMap.get(eventName));
            }
        }
    }

    private static Map<String, List> getEmptyFunnelResult(List<String> keyList, List<String> alias, String dimension, String dimensionValue)
    {

        Map<String, List> result = new HashMap<>();
        List<String> columnKeyList = new ArrayList<>();
        List<String> nameList = new ArrayList<>();
        List<Map<String, Object>> val = new ArrayList<>();

        if (StringUtil.isEmpty(dimension)) {
            columnKeyList.add("ds");
            nameList.add("日期");
        }
        else {
            columnKeyList.add(dimension);
            nameList.add(dimension);
        }

        if (StringUtil.isEmpty(dimensionValue)) {

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

            map.put(StringUtil.isEmpty(dimension) ? "ds" : dimension, "整体");
            map = getEmptyFunnelValueMap(map, columnKeyList, keyList, nameList);

            val.add(map);
        }
        else {

            String[] valueList = dimensionValue.split(",");
            for (String v : valueList) {

                Map<String, Object> map = new HashMap<>();
                map.put(dimension, v);
                map = getEmptyFunnelValueMap(map, columnKeyList, keyList, nameList);

                val.add(map);
            }
        }

        result.put("val", val);
        result.put("columnkey", columnKeyList);
        result.put("name", nameList);
        result.put("isempty", new ArrayList<>());

        return result;
    }

    private static Map<String, Object> getEmptyFunnelValueMap(Map<String, Object> map, List<String> columnKeyList, List<String> keyList, List<String> nameList)
    {

        for (int i = 0; i < keyList.size(); i++) {

            if (i != 0) {
                columnKeyList.add(keyList.get(i) + "lost");
                nameList.add("流失");
                map.put("rate_" + keyList.get(i) + "lost", 0d);
            }

            columnKeyList.add(keyList.get(i));
            nameList.add(keyList.get(i));
            map.put(keyList.get(i), 0);
            map.put("rate_" + keyList.get(i), i == 0 ? 100d : 0d);
        }

        return map;
    }

    public static Map<String, List> format4DetailList(Map<String, List> response, String startDate, int interval, String reportView)
    {

        Map<String, List> result = new HashMap<>();
        List<Map<String, Object>> valueResult = new ArrayList<>();
        List<String> columnKeyList = new ArrayList<>();
        List<String> nameList = new ArrayList<>();

        List<Map<String, Object>> valList = response.get("val");

        if (valList != null && valList.size() > 0) {
            Map<String, Object> valueMap = valList.get(0);
            List<Long> users = new ArrayList<>();
            for (String k : valueMap.keySet()) {
                if (!"isall0".equals(k)) {
                    JSONArray valArray = JSONArray.fromObject(valueMap.get(k));
                    for (Object o : valArray.toArray()) {
                        users.add(Long.valueOf(o.toString()));
                    }
                }
            }

            if (users.size() > 0) {

                int retentionInterval = getRetentions(reportView);
                List<Long> firstEventUsers = users.subList(users.size() - interval, users.size());
                List<Long> secondEventUsers = users.subList(0, users.size() - interval);

                for (int i = 0; i < interval; i++) {

                    Map<String, Object> innerMap = new HashMap<>();
                    if ("day".equals(reportView)) {
                        innerMap.put("ds", DateUtil.getBeforeDays(startDate, -i));
                    }
                    else if ("week".equals(reportView)) {
                        String weekStr = DateUtil.getAfterWeeks(startDate, i);
                        innerMap.put("ds", DateUtil.getFirstDayOfWeek(weekStr) + "~" + DateUtil.getLastDayOfWeek(weekStr));
                    }
                    else {
                        String monthStr = DateUtil.getAfterMonths(startDate, i);
                        innerMap.put("ds", DateUtil.getFirstdayOfMonth(monthStr) + "~" + DateUtil.getLastdayOfMonth(monthStr));
                    }

                    Long first = firstEventUsers.get(i);
                    innerMap.put("init", first);

                    if (!columnKeyList.contains("ds")) {
                        columnKeyList.add("ds");
                        nameList.add("日期");
                    }

                    if (!columnKeyList.contains("init")) {
                        columnKeyList.add("init");
                        nameList.add("初始人数");
                    }

                    for (int j = i * retentionInterval; j < (i + 1) * retentionInterval; j++) {

                        if (isDateValid(i == 0 ? j : j % (i * retentionInterval), reportView)) {
                            Long retention = secondEventUsers.get(j);
                            innerMap.put("retention" + (j % retentionInterval + 1), retention);

                            Double rate = first.equals(0L) ? 0d : retention * 100.0 / first;
                            BigDecimal rateb = BigDecimal.valueOf(rate);
                            Object ratebResult = rateb.setScale(2, BigDecimal.ROUND_HALF_UP);

                            innerMap.put("rate_" + "retention" + (j % retentionInterval + 1), ratebResult);

                            if (!columnKeyList.contains("retention" + (j % retentionInterval + 1))) {
                                columnKeyList.add("retention" + (j % retentionInterval + 1));
                                nameList.add((j % retentionInterval + 1) + getNameByReportView(reportView) + "后");
                            }
                        }
                    }
                    valueResult.add(innerMap);
                }
            }
        }

        if (valueResult.isEmpty()) {
            result = getEmptyRetentionResult("ds", true, reportView, null, null);
        }
        else {
            result.put("val", valueResult);
            result.put("columnkey", columnKeyList);
            result.put("name", nameList);
        }

        return result;
    }

    /**
     * 格式化来自报表系统的留存数据
     */
    public static Map<String, List> format4Retention(Map<String, List> response, int interval, String dimension, String reportView, String userGroup,
            String userGroupName, String dimensionValues)
    {

        Map<String, List> result = new HashMap<>();
        List<Map<String, Object>> valueResult = new ArrayList<>();
        List<String> columnKeyList = new ArrayList<>();
        List<String> nameList = new ArrayList<>();

        List<Map<String, Object>> valList = response.get("val");

        if (valList != null && valList.size() > 0) {
            List<String> dimensionValueList = new ArrayList<>();
            for (Map<String, Object> valueMap : valList) {

                String dimensionValue = "";
                String dimensionKey = "";
                List<Long> users = new ArrayList<>();

                for (String k : valueMap.keySet()) {

                    if (!"isall0".equals(k) && !k.equals(dimension)) {

                        JSONArray valArray = JSONArray.fromObject(valueMap.get(k));
                        for (Object o : valArray.toArray()) {
                            users.add(Long.valueOf(o.toString()));
                        }
                    }
                    else if (k.equals(dimension)) {

                        dimensionValue = valueMap.get(k).toString();
                        dimensionKey = valueMap.get(k).toString();
                        dimensionValueList.add(dimensionValue);
                    }
                }

                if (users.size() > 0) {

                    List<Long> firstEventUsers = users.subList(users.size() - interval, users.size());
                    List<Long> secondEventUsers = users.subList(0, users.size() - interval);

                    if (!StringUtil.isEmpty(dimension)) {
                        columnKeyList.add(dimension);
                        nameList.add("usergroup".equals(dimension) ? "用户组" : "维度");
                    }
                    else {
                        columnKeyList.add("ds");
                        nameList.add("日期");
                    }

                    columnKeyList.add("init");
                    nameList.add("初始日");

                    Map<String, Object> valueResultMap = deal1Retention(interval, firstEventUsers, secondEventUsers,
                            columnKeyList, nameList, dimension, dimensionValue, reportView, userGroup, userGroupName, dimensionKey);

                    valueResult.add(valueResultMap);
                }
            }
            if (!StringUtil.isEmpty(dimensionValues)) {
                String[] dimensionValueList4Web = dimensionValues.split(",");
                if (valueResult.size() < dimensionValueList4Web.length) {
                    for (String dv : dimensionValueList4Web) {
                        if (!dimensionValueList.contains(dv)) {
                            valueResult.addAll(getEmptyRetentionResult(dimension, false, reportView, userGroup, dv).get("val"));
                        }
                    }
                }
            }
        }

        if (valueResult.isEmpty()) {
            if (!StringUtil.isEmpty(userGroupName)) {
                dimensionValues = userGroupName;
            }
            result = getEmptyRetentionResult(dimension, false, reportView, userGroup, dimensionValues);
        }
        else {
            result.put("val", valueResult);
            result.put("columnkey", columnKeyList);
            result.put("name", nameList);
            result.put("key", nameList.isEmpty() ? new ArrayList<String>() : nameList.subList(1, nameList.size()));
        }

        return result;
    }

    /**
     * 获取空留存结果信息
     */
    private static Map<String, List> getEmptyRetentionResult(String dimention, boolean isDetail, String reportView,
            String userGroup, String dimensionValue)
    {

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

        if (!isDetail) {

            if (StringUtil.isEmpty(dimensionValue)) {

                Map<String, Object> map = new HashMap<>();
                map.put(StringUtil.isEmpty(dimention) ? "ds" : dimention, "整体");
                map = getEmptyRetentionValue(map, reportView);
                val.add(map);
            }
            else {

                String[] valueList = dimensionValue.split(",");
                for (String v : valueList) {
                    Map<String, Object> map = new HashMap<>();
                    if (!StringUtil.isEmpty(userGroup)) {
                        map.put(dimention + "id", userGroup);
                    }
                    map.put(dimention, v);
                    map = getEmptyRetentionValue(map, reportView);
                    val.add(map);
                }
            }
        }

        //columnkey
        List<String> columnKey = new ArrayList<>();
        columnKey.add(StringUtil.isEmpty(dimention) ? "ds" : dimention);
        columnKey.add("init");
        columnKey = getRetentionColumnKeyByView(columnKey, reportView);

        //name
        List<String> name = new ArrayList<>();
        name = getRetentionColumnNameByView(name, reportView);

        result.put("columnkey", columnKey);
        result.put("name", name);
        result.put("key", name.subList(1, name.size()));
        result.put("isempty", new ArrayList<>(1));
        result.put("val", val);

        return result;
    }

    /**
     * 获取留存的columnKey列表
     * created by sunhao 2017-05-12
     */
    private static List<String> getRetentionColumnKeyByView(List<String> columnKey, String reportView)
    {

        switch (reportView) {
            case VIEW_DAY:
                columnKey.add("retention1");
                columnKey.add("retention2");
                columnKey.add("retention3");
                columnKey.add("retention4");
                columnKey.add("retention5");
                columnKey.add("retention6");
                columnKey.add("retention7");
                columnKey.add("retention14");
                columnKey.add("retention30");
                break;
            case VIEW_WEEK:
                columnKey.add("retention1");
                columnKey.add("retention2");
                columnKey.add("retention3");
                columnKey.add("retention4");
                columnKey.add("retention5");
                columnKey.add("retention6");
                columnKey.add("retention7");
                columnKey.add("retention8");
                break;
            case VIEW_MONTH:
                columnKey.add("retention1");
                columnKey.add("retention2");
                columnKey.add("retention3");
                break;
        }

        return columnKey;
    }

    /**
     * 获取留存的列名列表
     * created by sunhao 2017-05-12
     */
    private static List<String> getRetentionColumnNameByView(List<String> name, String reportView)
    {

        name.add("日期");
        name.add("初始人数");

        switch (reportView) {
            case VIEW_DAY:
                name.add("1天后");
                name.add("2天后");
                name.add("3天后");
                name.add("4天后");
                name.add("5天后");
                name.add("6天后");
                name.add("7天后");
                name.add("14天后");
                name.add("30天后");
                break;
            case VIEW_WEEK:
                name.add("1周后");
                name.add("2周后");
                name.add("3周后");
                name.add("4周后");
                name.add("5周后");
                name.add("6周后");
                name.add("7周后");
                name.add("8周后");
                break;
            case VIEW_MONTH:
                name.add("1月后");
                name.add("2月后");
                name.add("3月后");
                break;
        }

        return name;
    }

    private static Map<String, Object> getEmptyRetentionValue(Map<String, Object> map, String reportView)
    {

        map.put("init", 0);

        switch (reportView) {
            case VIEW_DAY:
                map.put("retention1", 0);
                map.put("retention2", 0);
                map.put("retention3", 0);
                map.put("retention4", 0);
                map.put("retention5", 0);
                map.put("retention6", 0);
                map.put("retention7", 0);
                map.put("retention14", 0);
                map.put("retention30", 0d);
                map.put("rate_retention1", 0d);
                map.put("rate_retention2", 0d);
                map.put("rate_retention3", 0d);
                map.put("rate_retention4", 0d);
                map.put("rate_retention5", 0d);
                map.put("rate_retention6", 0d);
                map.put("rate_retention7", 0d);
                map.put("rate_retention14", 0d);
                map.put("rate_retention30", 0d);
                break;
            case VIEW_WEEK:
                map.put("retention1", 0);
                map.put("retention2", 0);
                map.put("retention3", 0);
                map.put("retention4", 0);
                map.put("retention5", 0);
                map.put("retention6", 0);
                map.put("retention7", 0);
                map.put("retention8", 0);
                map.put("rate_retention1", 0d);
                map.put("rate_retention2", 0d);
                map.put("rate_retention3", 0d);
                map.put("rate_retention4", 0d);
                map.put("rate_retention5", 0d);
                map.put("rate_retention6", 0d);
                map.put("rate_retention7", 0d);
                map.put("rate_retention8", 0d);
                break;
            case VIEW_MONTH:
                map.put("retention1", 0);
                map.put("retention2", 0);
                map.put("retention3", 0);
                map.put("rate_retention1", 0d);
                map.put("rate_retention2", 0d);
                map.put("rate_retention3", 0d);
                break;
        }

        return map;
    }

    public static Map<String, Object> getEmptyRetentionValue(String key, String usergroup, String value, String reportView)
    {

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

        map.put(key, value);
        if ("usergroup".equals(key)) {
            map.put("usergroupid", usergroup);
        }

        map = getEmptyRetentionValue(map, reportView);
        ;

        return map;
    }

    public static Map<String, Object> deal1Retention(int interval, List<Long> firstEventUsers, List<Long> secondEventUsers,
            List<String> columnKeyList, List<String> nameList, String dimension,
            String dimensionValue, String reportView, String userGroup,
            String userGroupName, String dimensionKey)
    {

        Map<String, Object> valueResultMap = new HashMap<>();
        Map<String, Long> innerMap = new HashMap<>();
        int retentionInterval = getRetentions(reportView);

        for (int i = 0; i < interval; i++) {

            Long first = !innerMap.containsKey("init") ? firstEventUsers.get(i) : innerMap.get("init") + firstEventUsers.get(i);
            innerMap.put("init", first);

            for (int j = i * retentionInterval; j < (i + 1) * retentionInterval; j++) {

                if (isDateValid(i == 0 ? j : j % (i * retentionInterval), reportView)) {

                    Long retention = !innerMap.containsKey("retention" + (j % retentionInterval + 1)) ? secondEventUsers.get(j) : innerMap.get("retention" + (j % retentionInterval + 1)) + secondEventUsers.get(j);
                    innerMap.put("retention" + (j % retentionInterval + 1), retention);

                    if (!columnKeyList.contains("retention" + (j % retentionInterval + 1))) {
                        columnKeyList.add("retention" + (j % retentionInterval + 1));
                        nameList.add((j % retentionInterval + 1) + getNameByReportView(reportView) + "后");
                    }
                }
            }
        }

        if (StringUtil.isEmpty(dimension)) {
            valueResultMap.put("ds", "整体");
        }
        else if (!StringUtil.isEmpty(userGroup)) {
            valueResultMap.put(dimension, userGroupName);
            valueResultMap.put(dimension + "id", userGroup);
        }
        else if (StringUtil.isEmpty(dimensionValue)) {
            valueResultMap.put(dimension, "整体");
        }
        else {
            valueResultMap.put(dimension, dimensionValue);
            valueResultMap.put(dimension + "_key", dimensionKey);
        }

        for (String key : innerMap.keySet()) {
            valueResultMap.put(key, innerMap.get(key));
            if (!key.equals("init")) {
                Double rate = innerMap.get("init").equals(0L) ? 0d : innerMap.get(key) * 100.0 / innerMap.get("init");
                BigDecimal rateb = BigDecimal.valueOf(rate);
                Object ratebResult = rateb.setScale(2, BigDecimal.ROUND_HALF_UP);
                valueResultMap.put("rate_" + key, ratebResult);
            }
        }

        return valueResultMap;
    }

    public static Map<String, List> formatRetentionByName(String responseJson, boolean isDetail, String reportView, String start,
            String end, String userGroup, String usergroupName, boolean isDemo)
    {
        Map<String, List> result = new HashMap<>();
        try {

            ReportListResult reportListResult = new ObjectMapper().readValue(responseJson, ReportListResult.class);

            if (reportListResult.getDesc() != null) {
                result.put("desc", reportListResult.getDesc());
            }
            List<String> nameList = reportListResult.getName();
            List<Map<String, Object>> val = reportListResult.getVal();
            List<Map<String, Object>> newVal = new ArrayList<>();
            List<String> valKeys = new ArrayList<>();

            result.put("columnkey", reportListResult.getColumnkey());
            result.put("name", nameList);
            result.put("key", nameList.subList(1, nameList.size()));
            result.put("val", reportListResult.getVal());

            for (Map<String, Object> v : val) {
                valKeys.add(v.get("ds").toString());
            }

            List<String> keys = getRetentionKeys(start, end, reportView);

            if (isDetail) {
                //补0
                for (String key : keys) {

                    if (valKeys.contains(key)) {

                        Map<String, Object> map = val.get(valKeys.indexOf(key));

                        if ("week".equals(reportView)) {
                            map.put("ds", key + "~" + DateUtil.getLastDayStrOfWeek(DateUtil.parseDate(key)));
                        }
                        else if ("month".equals(reportView)) {
                            map.put("ds", key + "~" + DateUtil.getLastDayOfMonth(DateUtil.parseDate(key)));
                        }

                        Map<String, Object> valueResultMap = new HashMap<>();
                        for (String k : map.keySet()) {

                            valueResultMap.put(k, map.get(k));

                            if (!k.equals("init") && !k.equals("ds") && !k.equals("isall0")) {
                                Double rate = map.get("init").equals(0) ? 0d : Integer.valueOf(map.get(k).toString()) * 100.0 / Integer.valueOf(map.get("init").toString());
                                BigDecimal rateb = BigDecimal.valueOf(rate);
                                Object ratebResult = rateb.setScale(2, BigDecimal.ROUND_HALF_UP);
                                valueResultMap.put("rate_" + k, ratebResult);
                            }
                        }

                        newVal.add(valueResultMap);
                    }
                    else {

                        String dsValue = key;

                        if ("week".equals(reportView)) {
                            dsValue = key + "~" + DateUtil.getLastDayStrOfWeek(DateUtil.parseDate(key));
                        }
                        else if ("month".equals(reportView)) {
                            dsValue = key + "~" + DateUtil.getLastDayOfMonth(DateUtil.parseDate(key));
                        }

                        newVal.add(getEmptyRetentionValue("ds", null, dsValue, reportView));
                    }
                }

                result.put("val", newVal);
            }
            else {
                //汇总
                if (!val.isEmpty()) {

                    Map<String, Object> map = new HashMap<>();
                    for (Map<String, Object> v : val) {
                        if (keys.contains(v.get("ds"))) {

                            for (String key : v.keySet()) {
                                if (!"ds".equals(key) && !"isall0".equals(key)) {
                                    map.put(key, !map.containsKey(key) ? v.get(key) : Integer.valueOf(map.get(key).toString()) + Integer.valueOf(v.get(key).toString()));
                                }
                            }
                        }
                    }

                    Map<String, Object> valueResultMap = new HashMap<>();
                    for (String key : map.keySet()) {

                        valueResultMap.put(key, map.get(key));
                        if (!key.equals("init")) {
                            Double rate = map.get("init").equals(0) ? 0d : Integer.valueOf(map.get(key).toString()) * 100.0 / Integer.valueOf(map.get("init").toString());
                            BigDecimal rateb = BigDecimal.valueOf(rate);
                            Object ratebResult = rateb.setScale(2, BigDecimal.ROUND_HALF_UP);
                            valueResultMap.put("rate_" + key, ratebResult);
                        }
                    }

                    if (!StringUtil.isEmpty(userGroup) && !StringUtil.isEmpty(usergroupName)) {

                        valueResultMap.put("usergroup", usergroupName);
                        valueResultMap.put("usergroupid", userGroup);
                        List<String> columnkey = result.get("columnkey");
                        columnkey.remove("ds");
                        columnkey.add(0, "usergroup");
                        List<String> name = result.get("name");
                        name.remove("日期");
                        name.add(0, "usergroup");
                        result.put("key", name.subList(1, name.size()));
                    }
                    else if (!StringUtil.isEmpty(userGroup)) {

                        valueResultMap.put("usergroup", "整体");
                        List<String> columnkey = result.get("columnkey");
                        columnkey.remove("ds");
                        columnkey.add(0, "usergroup");
                        List<String> name = result.get("name");
                        name.remove("日期");
                        name.add(0, "usergroup");
                        result.put("key", name.subList(1, name.size()));
                    }
                    else {
                        valueResultMap.put("ds", "整体");
                    }

                    newVal.add(valueResultMap);
                }

                result.put("val", newVal);

                if (newVal.isEmpty()) {
                    if (!StringUtil.isEmpty(userGroup)) {
                        newVal.add(getEmptyRetentionValue("usergroup", userGroup, usergroupName, reportView));
                        List<String> columnkey = result.get("columnkey");
                        columnkey.remove("ds");
                        columnkey.add(0, "usergroup");
                        List<String> name = result.get("name");
                        name.remove("日期");
                        name.add(0, "usergroup");
                        result.put("key", name.subList(1, name.size()));
                    }
                    else {
                        newVal.add(getEmptyRetentionValue("ds", null, "日期", reportView));
                    }
                }
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }

        return result;
    }

    private static List<String> getRetentionKeys(String start, String end, String reportView)
    {

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

        switch (reportView) {
            case VIEW_DAY:
                keys = DateUtil.getDateInterval(start, end);
                break;
            case VIEW_WEEK:
                List<String> weeks = DateUtil.getEveryWeek(start, end);
                for (String week : weeks) {
                    keys.add(DateUtil.getFirstDayOfWeek(week));
                }
                break;
            case VIEW_MONTH:
                List<String> months = DateUtil.getEveryMonth(start, end);
                for (String month : months) {
                    keys.add(DateUtil.getFirstdayOfMonth(month));
                }
                break;
        }

        return keys;
    }

    private static String getNameByReportView(String reportView)
    {

        String reportViewName = "日";

        switch (reportView) {
            case VIEW_DAY:
                reportViewName = "日";
                break;
            case VIEW_WEEK:
                reportViewName = "周";
                break;
            case VIEW_MONTH:
                reportViewName = "月";
                break;
        }

        return reportViewName;
    }

    private static boolean isDateValid(int day, String reportView)
    {

        List<Integer> dayList = new ArrayList<>();

        switch (reportView) {
            case VIEW_DAY:
                dayList.add(1);
                dayList.add(2);
                dayList.add(3);
                dayList.add(4);
                dayList.add(5);
                dayList.add(6);
                dayList.add(7);
                dayList.add(14);
                dayList.add(30);
                break;
            case VIEW_WEEK:
                dayList.add(1);
                dayList.add(2);
                dayList.add(3);
                dayList.add(4);
                dayList.add(5);
                dayList.add(6);
                dayList.add(7);
                dayList.add(8);
                break;
            case VIEW_MONTH:
                dayList.add(1);
                dayList.add(2);
                dayList.add(3);
                break;
        }

        return dayList.contains(day + 1);
    }

    /**
     * 构建只能路径查询SQL
     * select pathfind_sum(temp, 20, false)
     * from (select pathfind_count(xwhen, xwhat, 'install,startup,reged,loggedin,payment', 'payment', false, 3600, 20) as temp
     * from hive.tkio_orc_kudu.event_942da081fd89f2d691b4b55b203d6468
     * where ds >= '2017-06-01' and ds <= '2017-06-01' and
     * xwhat in ('install','startup','reged','loggedin','payment')
     * group by _deviceid);
     *
     * @param intelligentPath 参数model
     * @param appKey appKey
     * @return sql
     */
    public static String generateIntelligentPathSql(IntelligentPath intelligentPath, String appKey)
    {

        StringBuilder sqlWhere = new StringBuilder();
        String userGroupSql = "";

        String inEventsSql = excludeTargetEvent(intelligentPath.getEvents(), intelligentPath.getTargetEvent());
        Long session = intelligentPath.getSession() * intelligentPath.getSessionUnit();

        //解析事件筛选构建where
        JSONObject conditionObject = JSONObject.fromObject(intelligentPath.getEventCondition());

        String eventName = conditionObject.getString("event");
        String relation = conditionObject.getString("relation");

        sqlWhere.append(" or (xwhat = '").append(eventName).append("' ");

        //属性
        JSONArray paramArray = (JSONArray) conditionObject.get("params");

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

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

                sqlWhere.append(String.format("%s %s", i == 0 ? "and " : relation, StringUtil.getSql(attrName, attrValue, attrOperate, attrType, "")));
            }
        }

        sqlWhere.append(")");

        //用户分群
        if (null != intelligentPath.getUserGroup()) {
            userGroupSql = String.format(" and _deviceid in (select objectid as _deviceid from %s where  id='%s' and objecttype='_deviceid') ",
                    Constant.usergroupTable,
                    intelligentPath.getUserGroup());
        }

        //参数校验
        if (StringUtils.isEmpty(inEventsSql) || StringUtils.isEmpty(sqlWhere)) {
            return null;
        }

        //拼装sql
        return String.format("select pathfind_sum(temp, 10, %s) as path from (select pathfind2_count(xwhen, xwhat," +
                        " '%s'," +
                        " '%s'," +
                        " %s," +
                        " %s, 10) as temp " +
                        " from " + Constant.eventTable + "%s " +
                        " where $campaign _deviceid <> '00000000-0000-0000-0000-000000000000' and ds >= '$startDate' and ds <= '$endDate'  and (xwhat in (%s) " +
                        " %s)" +

                        // " %s group by _deviceid);",
                        " %s group by xwho);",

                intelligentPath.getStartOrEnd(),
                intelligentPath.getEvents(),
                intelligentPath.getTargetEvent(),
                intelligentPath.getStartOrEnd(),
                session,
                appKey,
                inEventsSql,
                sqlWhere,
                userGroupSql
        );
    }

    /**
     * 踢除目标事件
     */
    private static String excludeTargetEvent(String events, String targetEvent)
    {

        StringBuilder stringBuilder = new StringBuilder();

        String[] eventArray = events.split(",");

        for (String event : eventArray) {

            if (!targetEvent.equals(event)) {
                stringBuilder.append("'").append(event).append("',");
            }
        }

        return stringBuilder.length() == 0 ? null : stringBuilder.toString().substring(0, stringBuilder.length() - 1);
    }

    public static void main(String[] args)
    {
//		 String eventinfo =
//		 "[{event: \"Action\",relation:\"and\",params:[]},{event: \"payment\",relation:\"and\",params:[]}]";
//		 SqlUtil util = new SqlUtil();
//		 String sql = util.generateRetentionSql(eventinfo, "bfa099f35fb3b8158846a24e7066ca34");
//		 System.out.println(sql);

//		String response = "{\"val\": [{\"step1\": 100,\"step2\": 80,\"step3\": 50}],\"key\": []}";
//		String response = "{\"val\":[{\"_col0\":\"583,465,485,410,357,337,315,311,327,304,625,265,233,514,230,334,361,363,112,0,0,0,0,0,0,0,0,0,0,0,16601\",\"isall0\":false}],\"key\":[]}";
//		Map<String, List> result = SqlUtil.format4List(response, "2016-12-10", 1);
//		System.out.println(result);
//		System.out.println(SqlUtil.format4DetailList(response, "2016-12-10", 1));

//		String funnelEventInfo = "[{event: \"open\"},{event: \"search\"},{event: \"view\"},{event: \"cart\"}]";
//		String sql = SqlUtil.generateFunnelSqlTemp(funnelEventInfo, "2323", 3);
//		sql = sql.replace("$dimentionselect", "");
//		sql = sql.replace("$dimentionwhere", "");
//		sql = sql.replace("$usergroup", "");
//		sql = sql.replace("$dimentionaftergroupby", "");
//		sql = sql.replace("$dimentiongroupby", "");
//		System.out.println(sql);

        IntelligentPath intelligentPath = new IntelligentPath();
        intelligentPath.setStartOrEnd(false);
        intelligentPath.setTargetEvent("payment");
        intelligentPath.setEvents("install,payment");
        intelligentPath.setEventCondition("{\"event\":\"payment\",\"relation\":\"and\",\"params\":[{\"attr\":\"_country\",\"type\":\"string\",\"value\":\"中国\",\"operator\":\"=\"}]}");
        intelligentPath.setSession(3600L);

        System.out.println(generateIntelligentPathSql(intelligentPath, "942da081fd89f2d691b4b55b203d6468"));
    }
}
