package com.reyun.service.impl;

import com.reyun.dic.CustomMenuType;
import com.reyun.dic.LogEnumType;
import com.reyun.dic.OperateObjectEnumType;
import com.reyun.dic.RoleEnumType;
import com.reyun.model.*;
import com.reyun.repository.*;
import com.reyun.service.*;
import com.reyun.taskexecute.ReportCallable;
import com.reyun.util.*;

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.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
public class FunnelServiceImpl implements FunnelService {

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

    @Autowired
    AppRepository appRepository;
    @Autowired
    FunnelRepository funnelRepository;
    @Autowired
    AccountRepository accountRepository;
    @Autowired
    EventtableMetadataRepository eventtableMetadataRepository;
    @Autowired
    UserGroupRepository usergroupRepository;
    @Autowired
    EventRepository eventRepository;
    @Autowired
    UserLogService userLogService;
    @Autowired
    AuthService authService;
    @Autowired
    CommonParamService commonParamService;
    @Autowired
    CustomMenuTemplateRepository customMenuTemplateRepository;
    @Autowired
    ConfigParamService configparamService;

    @Autowired
    EventMetaRepository eventMetaRepository;

    @Autowired
    ReportService reportService;

    private final static String TEMPLATE_TYPE_FUNNEL = "funnel";

    @Override
    public Map<String, List> funnelReport(Long funnel, String startDate, String endDate, String usergroup, boolean isList,
                                          String dimention, Account account, boolean isDevice, boolean isProfile) {

        Funnel funnelObject = funnelRepository.findOne(funnel);
        App app = appRepository.findOne(funnelObject.getApp());

        userLogService.insertLog(account, LogEnumType.FIND.getCode(), LogEnumType.FIND.getName() + "漏斗报表" + funnelObject.getName(),
                funnelObject, app.getId(), OperateObjectEnumType.REPORT.getCode());


        List<Event> eventsAlias = eventRepository.findEventAliasByEvent(app.getAppkey(), Arrays.asList(funnelObject.getEvents().split(",")));
        Map<String, String> eventAliasMap = new HashMap<>();
        Map<String, CommonEvent> commonEventMap = commonParamService.findAllCommonEvent();
        for (Event event : eventsAlias) {
            EventMeta eventMeta = eventMetaRepository.findByEventId(event.getAppkey(), event.getEventName());
            String aliasStr = event.getEventName();
            if (eventMeta != null) {
                aliasStr = StringUtil.isEmpty(eventMeta.getAlias()) ? event.getEventName() : eventMeta.getAlias();
            }
            if (commonEventMap.containsKey(event.getEventName())) {
                aliasStr = commonEventMap.get(event.getEventName()).getAlias();
            }
            String eventName = event.getEventName();
            eventAliasMap.put(event.getEventName(), aliasStr);
        }

        String sqlTemp = funnelObject.getQuerySql();

        //数据权限过滤

        String demoApps = configparamService.getConfigParamByKey("demo_appkey");
        List<String> demoAppList = Arrays.asList(demoApps.split(","));
        sqlTemp = sqlTemp.replace("$dimentionwhere","");
        sqlTemp = sqlTemp.replace("$startdatetime", DateUtil.parseDate(startDate).getTime() / 1000 + "");
        sqlTemp = sqlTemp.replace("$startdate", startDate);
        int interval = DateUtil.getDateInterval(startDate, endDate).size();
        String firstEnddate = DateUtil.getBeforeDays(startDate, -interval);
        String secondEnddate = DateUtil.getBeforeDays(startDate, -(interval + funnelObject.getWindow() - 1));
        sqlTemp = sqlTemp.replace("$firstenddate", firstEnddate);
        sqlTemp = sqlTemp.replace("$secondenddate", secondEnddate);
        sqlTemp = sqlTemp.replace("$interval", interval + "");
        boolean isCompare = false;
        String dimentionKey = dimention;

        sqlTemp = sqlTemp.replaceAll("xwho", "a.xwho");
        sqlTemp = sqlTemp.replaceAll("xwhat", "a.xwhat");
        sqlTemp = sqlTemp.replaceAll("xwhen", "a.xwhen");
        sqlTemp = sqlTemp.replaceAll(" ds", " a.ds");
        sqlTemp = sqlTemp.replaceAll("_deviceid", "a._deviceid");


        String dataSource = "";
        String  firstdimentionselect="";
        String[] split = sqlTemp.split(" ");
        for (String s : split) {
            s.replaceAll(" ", "");
            if (s.startsWith("tkio")) {
                dataSource = s;
                break;
            }
        }


        if(!(sqlTemp.indexOf("_cid")==-1)){
            sqlTemp=sqlTemp.replace("_cid","a._cid");
        }

        if(!(sqlTemp.indexOf("_campaignid")==-1)){
            sqlTemp=sqlTemp.replace("_campaignid","a._campaignid");
        }



        Pattern pattern = Pattern.compile("\\$dimentionselect");
        Matcher matcher = pattern.matcher(sqlTemp);
        sqlTemp=matcher.replaceFirst("\\$firstdimentionselect");

        String afterReplace = new String(dataSource + " a \\$joinprofile");
        String beforeReplace = new String(dataSource);

        sqlTemp = sqlTemp.replaceAll(beforeReplace, afterReplace);

        String totalSql = sqlTemp;
        String usergroupName = "";

        if (StringUtil.isEmpty(usergroup)) {
            sqlTemp = sqlTemp.replace("$usergroup", "");
            totalSql = totalSql.replace("$usergroup", "");
        } else {
            usergroupName = usergroupRepository.findOne(Long.valueOf(usergroup)).getName();
            isCompare = true;
            totalSql = totalSql.replace("$usergroup", "");
            sqlTemp = sqlTemp
                    .replace(
                            "$usergroup",
                            String.format(
                                    " and a.xwho in (select objectid as xwho from %s where id='%s' and objecttype='xwho')",
                                    Constant.usergroupTable, usergroup));
        }

        if (StringUtil.isEmpty(dimention) || dimention.equals("usergroup")) {

            sqlTemp = sqlTemp.replace("$dimentionaftergroupby", "");
            sqlTemp = sqlTemp.replace("$dimentionselect", "");
            sqlTemp = sqlTemp.replace("$dimentionwhere", "");
            sqlTemp = sqlTemp.replace("$dimentiongroupby", "");
            sqlTemp = sqlTemp.replace("$joinprofile", "");
            sqlTemp = sqlTemp.replace("$firstdimentionselect", "");

            totalSql = totalSql.replace("$dimentionselect", "");
            totalSql = totalSql.replace("$dimentionwhere", "");
            totalSql = totalSql.replace("$dimentionaftergroupby", "");
            totalSql = totalSql.replace("$dimentiongroupby", "");
            totalSql = totalSql.replace("$joinprofile", "");
            totalSql = totalSql.replace("$firstdimentionselect", "");


        } else {
            isCompare = true;
            totalSql = totalSql.replace("$dimentionselect", "");
            totalSql = totalSql.replace("$dimentionwhere", "");
            totalSql = totalSql.replace("$dimentionaftergroupby", "");
            totalSql = totalSql.replace("$dimentiongroupby", "");
            totalSql = totalSql.replace("$joinprofile", "");
            totalSql = totalSql.replace("$firstdimentionselect", "");

            if (isProfile) {
                sqlTemp = sqlTemp.replace("$dimentionselect", "b." + dimention + ",");
                sqlTemp = sqlTemp.replace("$dimentionaftergroupby", "," + " b." + dimention);
                sqlTemp = sqlTemp.replace("$dimentiongroupby", " group by " + dimention);
                sqlTemp = sqlTemp.replace("$dimentionwhere", "");
                sqlTemp = sqlTemp.replace("$firstdimentionselect", dimention + ", ");

                String profileSource = Constant.profileTable + app.getAppkey();
                sqlTemp = sqlTemp.replace("$joinprofile", "right join " + profileSource + " b on a.xwho=b.xwho");
            }else {
                sqlTemp = sqlTemp.replace("$dimentionselect","a." + dimention + ",");
                sqlTemp = sqlTemp.replace("$dimentionaftergroupby", "," + " a." + dimention);
                sqlTemp = sqlTemp.replace("$dimentiongroupby", " group by " + dimention);
                sqlTemp = sqlTemp.replace("$joinprofile", "");
                sqlTemp = sqlTemp.replace("$dimentionwhere", "");
                sqlTemp = sqlTemp.replace("$firstdimentionselect", dimention + ", ");
            }
        }



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

        try {

            if (!StringUtil.isEmpty(usergroup)) {
                dimentionKey = "usergroup";
            }

            if (isCompare) {
                //创建一个线程池
                ExecutorService pool = Executors.newFixedThreadPool(2);
                //创建两个有返回值的任务
                ReportCallable c1 = new ReportCallable("funnel", totalSql, funnelObject, startDate, endDate, dimentionKey,
                        false, true, interval, "day", null, "", eventAliasMap, null, app.getAppkey());
                ReportCallable c2 = new ReportCallable("funnel", sqlTemp, funnelObject, startDate, endDate, dimentionKey,
                        isCompare, false, interval, "day", usergroup, usergroupName, eventAliasMap, null, app.getAppkey());
                //执行任务并获取Future对象
                Future<Map<String, List>> f1 = pool.submit(c1);
                Future<Map<String, List>> f2 = pool.submit(c2);
                //从Future对象上获取任务的返回值，并输出到控制台
                try {
                    result = f1.get();
                    List<Map<String, Object>> val1 = result.get("val");
                    if (!result.containsKey("isempty")) {
                        Map<String, List> result2 = f2.get();
                        List<Map<String, Object>> val2 = result2.get("val");
                        val1.addAll(val2);
                    }
                    result.put("val", val1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
                //关闭线程池
                pool.shutdown();
            } else {

                Map<String, String> conditions = new HashMap<String, String>();
                conditions.put("sql", sqlTemp);
                conditions.put("dbtype", "presto");
                conditions.put("datatype", "list");
                conditions.put("reportname", "funnel");
                conditions.put("appid", app.getAppkey());


                Map<String, List> responseJson = reportService.reportBySql(conditions);


                result = SqlUtil.format4Funnel(responseJson, Arrays.asList(funnelObject.getEvents().split(",")), isCompare,
                        dimentionKey, startDate, endDate, false, usergroup, usergroupName, eventAliasMap, null);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


        return result;
    }

    /**
     * 漏斗分享成看单之后查询数据
     * created by sunhao 20170426
     */
    @Override
    public Map<String, List> funnelDataQuery(String startDate, String endDate, String eventInfo, String events, int window,
                                             Long appId, boolean isDevice) {

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

        App app = appRepository.findOne(appId);

        //eventAliasMap
        List<Event> eventsAlias = eventRepository.findEventAliasByEvent(app.getAppkey(), Arrays.asList(events.split(",")));
        Map<String, String> eventAliasMap = new HashMap<>();
        Map<String, CommonEvent> commonEventMap = commonParamService.findAllCommonEvent();
        for (Event event : eventsAlias) {
            EventMeta eventMeta = eventMetaRepository.findByEventId(event.getAppkey(), event.getEventName());
            String alias = event.getEventName();
            if (eventMeta != null) {
                alias = StringUtil.isEmpty(eventMeta.getAlias()) ? event.getEventName() : eventMeta.getAlias();
            }
            if (commonEventMap.containsKey(event.getEventName())) {
                alias = commonEventMap.get(event.getEventName()).getAlias();
            }
            eventAliasMap.put(event.getEventName(), alias);
        }

        //生成SQL
        String sqlTemp = SqlUtil.generateFunnelSqlTemp(eventInfo, app.getAppkey(), window);

        //日期替换
        int interval = DateUtil.getDateInterval(startDate, endDate).size();
        String firstEndDate = DateUtil.getBeforeDays(startDate, -interval);
        String secondEndDate = DateUtil.getBeforeDays(startDate, -(interval + window - 1));

        sqlTemp = sqlTemp.replace("$startdatetime", DateUtil.parseDate(startDate).getTime() / 1000 + "");
        sqlTemp = sqlTemp.replace("$startdate", startDate);
        sqlTemp = sqlTemp.replace("$firstenddate", firstEndDate);
        sqlTemp = sqlTemp.replace("$secondenddate", secondEndDate);
        sqlTemp = sqlTemp.replace("$interval", interval + "");

        //默认查询没有用户组
        sqlTemp = sqlTemp.replace("$usergroup", "");

        //默认查询没有分类维度数据
        sqlTemp = sqlTemp.replace("$dimentionselect", "");
        sqlTemp = sqlTemp.replace("$dimentionwhere", "");
        sqlTemp = sqlTemp.replace("$dimentionaftergroupby", "");
        sqlTemp = sqlTemp.replace("$dimentiongroupby", "");

        if (isDevice) {
            sqlTemp = sqlTemp.replace("xwho", "_deviceid");
        }

        try {

            Map<String, String> conditions = new HashMap<String, String>();
            conditions.put("sql", sqlTemp);
            conditions.put("dbtype", "presto");
            conditions.put("datatype", "list");
            conditions.put("appid", app.getAppkey());

            Map<String, List> responseJson = reportService.reportBySql(conditions);


            result = SqlUtil.format4Funnel(responseJson, Arrays.asList(events.split(",")), false, "", startDate,
                    endDate, false, "", "", eventAliasMap, null);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }

    @Override
    public Funnel create(Funnel funnel, Long account) {
        funnel.setAccount(account);
        funnel.setCreateAccount(account);
        App app = appRepository.findOne(funnel.getApp());
        String sqlTemp = SqlUtil.generateFunnelSqlTemp(funnel.getEventInfo(), app.getAppkey(), funnel.getWindow());
        funnel.setQuerySql(sqlTemp);

        return funnelRepository.save(funnel);
    }

    @Override
    public Funnel update(Funnel funnel, Long account) {
        Funnel dbFunnel = funnelRepository.findOne(funnel.getId());
        dbFunnel.setName(funnel.getName());
        dbFunnel.setEventInfo(funnel.getEventInfo());
        dbFunnel.setEvents(funnel.getEvents());
        dbFunnel.setModifyTime(new Date());
        dbFunnel.setModifyAccount(account);
        dbFunnel.setWindow(funnel.getWindow());

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

        String sqlTemp = SqlUtil.generateFunnelSqlTemp(funnel.getEventInfo(), app.getAppkey(), funnel.getWindow());
        dbFunnel.setQuerySql(sqlTemp);

        List<CustomMenuTemplate> templateList = customMenuTemplateRepository.findAllTemplateByOriginal(dbFunnel.getId(), TEMPLATE_TYPE_FUNNEL);
        if(ValidateUtil.isValid(templateList)){
            List<CustomMenuTemplate> list = new ArrayList<>();
            for(CustomMenuTemplate cu : templateList){
                cu.setName(dbFunnel.getName());
                list.add(cu);
            }
            customMenuTemplateRepository.save(list);
        }

        return funnelRepository.save(dbFunnel);
    }

    @Override
    public Funnel delete(Long id, Long account) {
        Funnel dbFunnel = funnelRepository.findOne(id);
        dbFunnel.setModifyAccount(account);
        dbFunnel.setModifyTime(new Date());
        dbFunnel.setDelFlag(true);

        //查看是否存在看单之中,如果存在则删除。
        List<Long> templateList = customMenuTemplateRepository.findTemplateByOriginal(id, CustomMenuType.FUNNEL.getKey());
        if(!CollectionUtils.isEmpty(templateList)){
            customMenuTemplateRepository.deleteTemplateByIds(account, templateList);
        }

        return funnelRepository.save(dbFunnel);
    }

    @Override
    public Funnel findById(Long id) {
        return funnelRepository.findOne(id);
    }

    @Override
    public List<Funnel> findByApp(Long app) {
        List<Funnel> funnelList = funnelRepository.findAllByApp(app);
        List<Long> account = new ArrayList<>();
        for (Funnel f : funnelList) {
            account.add(f.getCreateAccount());
        }
        Iterable<Account> accountList = accountRepository.findAll(account);
        Iterator<Account> a = accountList.iterator();
        Map<Long, String> idNameMap = new HashMap<>();
        while (a.hasNext()) {
            Account aObject = a.next();
            idNameMap.put(aObject.getId(), aObject.getEmail());
        }
        for (Funnel f : funnelList) {
            f.setcAccount(idNameMap.get(f.getCreateAccount()));
        }
        return funnelList;
    }

    @Override
    public Funnel validName(String name, Long app) {
        return funnelRepository.findByNameAndApp(name, app);
    }

}
