package com.reyun.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import com.reyun.dic.ReportEnumType;
import com.reyun.dic.RoleEnumType;
import com.reyun.model.*;
import com.reyun.repository.AccountRepository;
import com.reyun.repository.AppRepository;
import com.reyun.repository.IntelligentPathRepository;
import com.reyun.service.AuthService;
import com.reyun.service.ConfigParamService;
import com.reyun.service.EventService;
import com.reyun.service.IntelligentPathService;
import com.reyun.service.ReportService;
import com.reyun.util.Constant;
import com.reyun.util.HttpClientUtil;
import com.reyun.util.SqlUtil;
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.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * Created by sunhao on 17/7/18.
 * desc:智能路径service implements
 */
@Service
public class IntelligentPathServiceImpl implements IntelligentPathService {

    private static Logger logger = LoggerFactory.getLogger(IntelligentPathServiceImpl.class);

    //查询URI
    private final static String URI_REPORT_BY_SQL = "/api/trackingio/bysql";

    private final static int MAX_EVENT_NUM = 5;
    //其他事件节点
    private final static String OTHER_NODE_NAME = "其他";
    private final static String OTHER_NODE_ID = "NULL";

    private final static String ROOT_NODE_FLAG = "0";

    private final static String COMMA_SPLIT = ",";
    private final static String UNDERLINE_SPLIT = "_";
    private final static String COLON_SPLIT = ":";


    @Autowired
    ConfigParamService configparamService;

    @Autowired
    EventService eventService;

    @Autowired
    IntelligentPathRepository intelligentPathRepository;

    @Autowired
    AppRepository appRepository;

    @Autowired
    AccountRepository accountRepository;

    @Autowired
    AuthService authService;

    @Autowired ReportService reportService;

    /**
     * 创建智能路径查询
     */
    @Override
    public IntelligentPath createIntelligentPath(Account loginAccount, IntelligentPath intelligentPath) {

        //存储数据
        intelligentPath.setDelFlag(false);
        intelligentPath.setCreateAccount(loginAccount.getId());
        intelligentPath.setCreateTime(new Date());
        intelligentPath.setModifyAccount(loginAccount.getId());
        intelligentPath.setModifyTime(new Date());

        return intelligentPathRepository.save(intelligentPath);
    }

    /**
     * 修改智能路径查询
     */
    @Override
    public IntelligentPath updateIntelligentPath(Account loginAccount, IntelligentPath source) {

        IntelligentPath intelligentPath = intelligentPathRepository.findOne(source.getId());

        intelligentPath.setModifyAccount(loginAccount.getId());
        intelligentPath.setModifyTime(new Date());

        intelligentPath.setSession(source.getSession());
        intelligentPath.setSessionUnit(source.getSessionUnit());
        intelligentPath.setEventCondition(source.getEventCondition());
        intelligentPath.setTargetEvent(source.getTargetEvent());
        intelligentPath.setEvents(source.getEvents());
        intelligentPath.setUserGroup(source.getUserGroup());
        intelligentPath.setStartOrEnd(source.getStartOrEnd());

        return intelligentPathRepository.save(intelligentPath);
    }

    /**
     * 列出所有有效智能路径
     */
    @Override
    public List<IntelligentPath> listIntelligentPath(Account loginAccount, Long appId) {

        return intelligentPathRepository.findActiveByApp(appId);
    }

    /**
     * 删除智能路径
     */
    @Override
    public boolean deleteIntelligentPath(Account loginAccount, Long intelligentPathId) {

        return intelligentPathRepository.deleteById(intelligentPathId, loginAccount.getId()) > 0;
    }

    /**
     * 智能路径查询数据
     */
    @Override
    @Cacheable(value = "reportCache", key = "'eventstatstotal'+#loginAccount+#intelligentPath+#isCache", condition = "#isCache == 1")
    public JSONObject queryIntelligentPath(Account loginAccount, IntelligentPath intelligentPath, int isCache) {

        JSONObject resultObject = new JSONObject();

        List<IntelligentPathNode> intelligentPathList = new ArrayList<>();

        App app = appRepository.findOne(intelligentPath.getApp());

        if (null != app) {

            String appKey = app.getAppkey();

            String querySql = SqlUtil.generateIntelligentPathSql(intelligentPath, appKey);

            if (!StringUtils.isEmpty(querySql)) {

                querySql = querySql.replace("$campaign","");
                querySql = querySql.replace("$startDate", intelligentPath.getStartDate()).replace("$endDate", intelligentPath.getEndDate());

                Map<String, String> conditions = new HashMap<>();
                conditions.put("sql", querySql);
                conditions.put("dbtype", "presto");
                conditions.put("datatype", ReportEnumType.LIST.getCode());
                conditions.put("reportname", "intelligentPath");
                conditions.put("appid", app.getAppkey());

                //demo账号处理
                String demoApps = configparamService.getConfigParamByKey("demo_appkey");
                List<String> demoAppList = Arrays.asList(demoApps.split(","));
                Map<String, List> responseJson = new HashMap<>();
                //demo账号处理
                if (demoAppList.contains(appKey)) {
                    try {
                        String url = Constant.demoUrl + "/api/trackingio/intelligentPath/" + appKey;
                        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......");
                    }
                } else {
                    responseJson = reportService.reportBySql(conditions);
                }

                try {
                    List<Map<String, Object>> valueList = responseJson.get("val");

                    if (!CollectionUtils.isEmpty(valueList)) {

                        JSONArray array = JSONArray.fromObject(valueList.get(0).get("path"));

                        List<String> pathList = (List) array;

                        String[] eventArray = this.getEventNameAliasByEvent(app.getAppkey(),intelligentPath.getEvents());

                        intelligentPathList = this.intelligentPathDataParse(eventArray, pathList, intelligentPath.getStartOrEnd());
                    }

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

            } else {
                logger.error("intellegent path query sql error!");
            }
        }

        resultObject.put("type", intelligentPath.getStartOrEnd());
        resultObject.put("value", intelligentPathList);

        return resultObject;
    }


    /**
     * 智能路径数据转换
     * pathList 示例 ["3_1,4_0:4227","1_2,3_1:2433","3_2,3_1:1590","3_3,1_2:1347","1_4,3_3:1268","3_4,3_3:1217","3_3,3_2:1062","3_5,3_4:1048","1_3,3_2:999","3_6,3_5:961","3_7,3_6:859","4_1,4_0:841","1_6,3_5:828","1_5,3_4:827","3_8,3_7:807","3_9,3_8:742","3_5,1_4:731","1_7,3_6:638","3_4,1_3:625","1_8,3_7:549","3_6,1_5:529","3_2,4_1:497","1_9,3_8:491","3_7,1_6:488","3_8,1_7:414","3_9,1_8:389","4_2,4_1:329","3_3,4_2:229","1_3,1_2:188","3_4,4_3:179","4_3,4_2:176","1_5,1_4:172","1_4,1_3:167","1_7,1_6:160","1_1,4_0:160","3_5,4_4:147","1_6,1_5:147","4_4,4_3:145","1_8,1_7:137","4_3,1_2:111","3_6,4_5:109","3_2,1_1:99","1_9,1_8:98","3_8,4_7:84","4_5,4_4:82","4_2,3_1:81","3_7,4_6:81","4_5,1_4:59","4_6,4_5:57","3_9,4_8:57","4_7,4_6:53","4_4,1_3:52","4_3,3_2:48","1_2,1_1:43","4_7,1_6:41","4_6,3_5:40","4_4,3_3:39","4_6,1_5:38","4_8,4_7:37","4_5,3_4:34","4_9,4_8:31","4_8,1_7:29","4_7,3_6:27","4_9,3_8:26","4_8,3_7:19","4_9,1_8:19","2_7,3_6:16","2_5,3_4:16","2_6,3_5:13","2_3,3_2:11","0_4,1_3:11","3_4,2_3:10","0_6,1_5:10","2_4,3_3:9","3_8,2_7:9","3_6,2_5:9","2_8,3_7:8","0_8,1_7:7","4_8,2_7:7","1_2,4_1:7","3_5,0_4:6","4_7,2_6:6","4_6,2_5:6","0_3,1_2:6","0_7,1_6:5","0_9,1_8:5","0_5,1_4:5","1_7,2_6:5","1_5,2_4:5","3_7,0_6:5","1_4,4_3:5","3_6,0_5:4","4_9,2_8:4","2_2,3_1:4","4_2,1_1:4","1_5,4_4:3","3_9,2_8:3","3_7,2_6:3","3_5,2_4:3","2_9,3_8:3","3_9,0_8:3","2_9,1_8:2","3_4,0_3:2","3_3,2_2:2","1_3,2_2:2","1_6,4_5:2","1_7,4_6:1","1_7,0_6:1","1_8,2_7:1","4_5,2_4:1","2_7,1_6:1","3_8,0_7:1","1_5,0_4:1","1_6,2_5:1","1_3,4_2:1","1_4,2_3:1","1_9,2_8:1","2_3,1_2:1","1_8,0_7:1","1_8,4_7:1","2_6,1_5:1","4_4,2_3:1"]
     *
     * @return List<IntelligentPathNode>
     */
    private List<IntelligentPathNode> intelligentPathDataParse(String[] eventsArray, List<String> pathList, boolean startOrEnd) {

        List<IntelligentPathNode> nodeList = new ArrayList<>();

        //rootNode
        String rootNode = null;

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

        Map<String, Long> nodeTotalNumberMap = new HashMap<>();

        for (String path : pathList) {

            String[] nodeArray = path.split(COMMA_SPLIT);

            String[] secondArray = nodeArray[1].split(COLON_SPLIT);

            //第一个节点
            String firstNodeStr = nodeArray[0];
            String[] firstNodeStrArray = firstNodeStr.split(UNDERLINE_SPLIT);
            //第二个节点
            String secondNodeStr = secondArray[0];
            String[] secondNodeStrArray = secondNodeStr.split(UNDERLINE_SPLIT);
            //会话数
            Long number = Long.parseLong(secondArray[1]);
            //层级;当前节点事件的index;父节点;当前节点ID.(初始事件和结束事件在presto结果集里面父子节点位置调换了)
            int floor = Integer.parseInt(startOrEnd ? secondNodeStrArray[1] : firstNodeStrArray[1]);
            int eventIndex = Integer.parseInt(startOrEnd ? secondNodeStrArray[0] : firstNodeStrArray[0]);
            String parentStr = startOrEnd ? firstNodeStr : secondNodeStr;
            String idStr = startOrEnd ? secondNodeStr : firstNodeStr;

            //根节点
            if (null == rootNode && ROOT_NODE_FLAG.equals(parentStr.split(UNDERLINE_SPLIT)[1])) {
                rootNode = parentStr;
            }

            //按母节点分类
            List<IntelligentPathNode> intelligentPathNodeList = parentNodeMap.get(parentStr);
            intelligentPathNodeList = null != intelligentPathNodeList ? intelligentPathNodeList : new ArrayList<IntelligentPathNode>();

            //构建node
            IntelligentPathNode node = new IntelligentPathNode(idStr, eventsArray[eventIndex], number, parentStr, floor + 1);

            //插入排序
            int index = 0;

            for (index = 0; index < intelligentPathNodeList.size(); index++) {

                IntelligentPathNode nodeTemp = intelligentPathNodeList.get(index);

                if (nodeTemp.getNumber() <= node.getNumber()) {
                    break;
                }
            }

            if (index == intelligentPathNodeList.size()) {
                intelligentPathNodeList.add(node);
            } else {
                intelligentPathNodeList.add(index - 1 < 0 ? 0 : index - 1, node);
            }

            //汇总处理
            Long total = nodeTotalNumberMap.get(parentStr);
            nodeTotalNumberMap.put(parentStr, null != total ? total + number : number);

            parentNodeMap.put(parentStr, intelligentPathNodeList);
        }

        //加入rootNode
        if (!StringUtils.isEmpty(rootNode)) {
            IntelligentPathNode rootPathNode = new IntelligentPathNode(rootNode, eventsArray[Integer.parseInt(rootNode.split(UNDERLINE_SPLIT)[0])], 0L, null, 1);
            rootPathNode.setNumber(nodeTotalNumberMap.get(rootPathNode.getId()));
            rootPathNode.setTotalNumber(rootPathNode.getNumber());
            nodeList.add(rootPathNode);
        }

        //计算百分比
        nodeList.addAll(this.calculatePercent(parentNodeMap, nodeTotalNumberMap));

        return nodeList;
    }

    /**
     * 获取事件的中文名
     */
    private String[] getEventNameAliasByEvent(String appKey, String events) {

        String[] eventArray = events.split(COMMA_SPLIT);

        //获取事件别名MAP
        Map<String, String> eventNameAliasMap = eventService.findEventNameAliasMap(appKey, Lists.newArrayList(eventArray));

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

            if (eventNameAliasMap.containsKey(eventArray[i])) {
                eventArray[i] = eventNameAliasMap.get(eventArray[i]);
            }
        }

        return eventArray;
    }

    /**
     * 计算每个节点的百分比
     */
    private List<IntelligentPathNode> calculatePercent(Map<String, List<IntelligentPathNode>> parentNodeMap, Map<String, Long> nodeTotalNumberMap) {

        List<IntelligentPathNode> nodeList = new ArrayList<>();

        Set<String> removeNodeSet = new HashSet<>();

        for(String parentKey : parentNodeMap.keySet()){

            //如果父节点删除,子节点也删除
            if(removeNodeSet.contains(parentKey)){
                continue;
            }

            List<IntelligentPathNode> intelligentPathNodeList = parentNodeMap.get(parentKey);

            Iterator<IntelligentPathNode> iterator = intelligentPathNodeList.iterator();

            IntelligentPathNode node = null;

            int i = 0;

            //超过数量后的其他节点
            if (intelligentPathNodeList.size() > MAX_EVENT_NUM) {
                node = intelligentPathNodeList.get(MAX_EVENT_NUM);

                removeNodeSet.add(node.getId());

                node.setName(OTHER_NODE_NAME);
                node.setId(OTHER_NODE_ID);
            }

            //处理子节点
            while (iterator.hasNext()) {

                IntelligentPathNode intelligentPathNode = iterator.next();
                Long totalNumber = nodeTotalNumberMap.get(intelligentPathNode.getParent());

                if (i > MAX_EVENT_NUM && null != node) {

                    removeNodeSet.add(intelligentPathNode.getId());

                    node.setNumber(node.getNumber() + intelligentPathNode.getNumber());
                    node.setPercent(node.getNumber() * 1d / totalNumber);
                    iterator.remove();

                } else {

                    intelligentPathNode.setPercent(intelligentPathNode.getNumber() * 1d / totalNumber);
                    nodeList.add(intelligentPathNode);
                }

                intelligentPathNode.setTotalNumber(totalNumber);

                i++;
            }

        }

        return nodeList;
    }

    /**
     * 校验智能路径名字是否重复
     */
    @Override
    public boolean checkIntelligentPathName(Long accountId, Long id, Long appId, String name) {

        boolean result = true;

        if (null != id) {
            IntelligentPath intelligentPath = intelligentPathRepository.findOne(id);

            if (!name.equals(intelligentPath.getName())) {

                List<IntelligentPath> intelligentPathList = intelligentPathRepository.findByNameApp(intelligentPath.getApp(), name, accountId);

                result = CollectionUtils.isEmpty(intelligentPathList);
            }

        } else if (null != appId) {

            List<IntelligentPath> intelligentPathList = intelligentPathRepository.findByNameApp(appId, name, accountId);

            result = CollectionUtils.isEmpty(intelligentPathList);
        }

        return result;
    }
}
