package com.liangjian.dataplatform.member.service.impl;

import com.liangjian.dataplatform.member.dao.primary.MetaRelRepository;
import com.liangjian.dataplatform.member.dao.primary.TagRuleRepository;
import com.liangjian.dataplatform.member.entity.po.primary.TagRule;
import com.liangjian.dataplatform.member.entity.vo.MetaRelVo;
import com.liangjian.dataplatform.member.entity.vo.TagRuleVo;
import com.liangjian.dataplatform.member.entity.vo.datanodes.DataNode;
import com.liangjian.dataplatform.member.entity.vo.datanodes.PDM;
import com.liangjian.dataplatform.member.entity.vo.datanodes.PDMColumn;
import com.liangjian.dataplatform.member.entity.vo.tagrules.BinOperation;
import com.liangjian.dataplatform.member.entity.vo.tagrules.ConditionNode;
import com.liangjian.dataplatform.member.entity.vo.tagrules.SqlAndResult;
import com.liangjian.dataplatform.member.entity.vo.tagrules.SqlContext;
import com.liangjian.dataplatform.member.service.IMetaCoreQueryService;
import com.liangjian.dataplatform.member.service.ITagRuleSqlService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: 黄子奇
 * @Date: 2019/9/21 10:51
 */
@Service
public class TagRuleSqlService implements ITagRuleSqlService {
    @Autowired
    private IMetaCoreQueryService iMetaCoreQueryService;
    @Autowired
    private MetaRelRepository metaRelRepository;
    @Autowired
    private TagRuleRepository tagRuleRepository;

    @Override
    public SqlContext makeContext(PDM pdm) {
        //获取子表
        Map<String,String> tableAlias = new HashMap<>();
        Map<String,String> tableNames = new HashMap<>();
        tableAlias.put(pdm.getId(), "f");
        List<DataNode> children = iMetaCoreQueryService.getRelaChildTables(pdm.getId());
        for (int i = 0; i< children.size(); i++) {
            //子表id，别名对应
            tableAlias.put(children.get(i).getId(), "c"+i);
            //子表id，名称对应
            tableNames.put(children.get(i).getId(), children.get(i).getName());
        }

        //关联关系
        Map<String, List<SqlContext.RelEntity>> joinCol = new HashMap<>();
        Map<String,List<MetaRelVo>> relations = metaRelRepository.queryBySrc(pdm.getId())
                .stream()
                .map(MetaRelVo::fromMetaRel)
                .collect(Collectors.groupingBy(MetaRelVo::getDest));
        relations.forEach((k,v) -> {
            List<SqlContext.RelEntity> entities = v.stream()
                    .map(i -> new SqlContext.RelEntity(i.getDestName(), i.getSrcName()))
                    .collect(Collectors.toList());
            joinCol.put(k,entities);
        });

        SqlContext context = new SqlContext(pdm.getId(),joinCol,tableAlias,tableNames);
        return context;
    }

    @Override
    public String toSqlWhere(ConditionNode conditionNode, SqlContext context) {
        //默认是1，条件产出
        if(context.getType() == null || context.getType().equals(1) ){
            String where = conditionNode.getLogicTree().toSql(context);
            return "WHERE " + where;
        } else {
            //直接产出
            BinOperation node = conditionNode.getLogicTree().getNode();
            String tableId = node.getLeftTableId();
            PDMColumn column = node.getLeft();
            String tableAlias = context.getTableAlias(tableId);
            String where = String.format("%s.%s IS NOT null", tableAlias,column.getName());
            return "WHERE " + where;
        }
    }

    @Override
    public String toSqlJoin(SqlContext context) {
        String joinBone = "LEFT JOIN %s ";
        String onBone = "%s.%s = %s.%s"; //f1.r1 = c1.r1
        Map<String, List<SqlContext.RelEntity>> joinCol = context.getJoinColumnsRel();
        List<String> joins = new ArrayList<>();
        joinCol.forEach((k,v) -> {
            Optional<String> onStr = v.stream().map(i -> {
                String fatherAlias = context.getTableAlias(context.getMainPdmId());
                String childAlias = context.getTableAlias(k);
                String childName = context.getTableName(k);
                String childOn = String.format(onBone,fatherAlias,i.srcCol,childAlias,i.destCol);
                childOn = childName + " " + childAlias + " ON " + childOn; //childName c1 on f.r1 = c1.r1
                return childOn;
            }).reduce((i,j) -> i + " AND " + j); // childName c1 on f.r1 = c1.r1 and f.r2 = c1.r2
            onStr.ifPresent(i -> joins.add(i));
        });
        String joinStr = joins.stream()
                .map(i -> String.format(joinBone, i))
                .reduce((i,j) -> i+j).orElse(""); //有可能这个context没有关联关系
        return joinStr;
    }

    @Override
    public String toSqlSelect(SqlContext context, List<String> cols) {
        String mainAlias = context.getTableAlias(context.getMainPdmId());
        String colStr = mainAlias + ".*";
        if(cols != null && !cols.isEmpty()) {
            colStr = cols.stream().map(i -> mainAlias + "." + i).reduce((i,j) -> i+" , "+j).get();
        }
        return "SELECT " + colStr;
    }

    @Override
    public List<SqlAndResult> getSqlAndResult(TagRuleVo vo, List<String> cols, Integer type) {
        PDM pdm = vo.getPdm();
        SqlContext context = makeContext(pdm);
        context.setType(type);
        List<ConditionNode> conditionNodes = vo.getOutputEntity();
        List<SqlAndResult> results = conditionNodes.stream().map(c -> {
            String select = toSqlSelect(context, cols); // SELECT f.*
            String from = String.format(" FROM %s %s", pdm.getName(), context.getTableAlias(pdm.getId())); // FROM maintable f
            String join = toSqlJoin(context); //LEFT JOIN children c1 ON f.r = c1.r ...
            String where = toSqlWhere((ConditionNode) c, context); // WHERE f.a > xxx AND c1.b < yyy ...
            String sql = String.format("%s %s %s %s", select, from, join, where);

            return new SqlAndResult(sql,c.getResult());
        }).collect(Collectors.toList());
        return results;
    }

    @Override
    public List<SqlAndResult> getSqlAndResult(String id, List<String> cols) {
        TagRule tagRule = tagRuleRepository.queryById(id);
        TagRuleVo vo = TagRuleVo.transfer(tagRule);
        return getSqlAndResult(vo,cols,tagRule.getOutputType());
    }
}
