package com.ebts.generator.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ebts.common.utils.SecurityUtils;
import com.ebts.common.utils.ServerResult;
import com.ebts.generator.dao.QueryDao;
import com.ebts.generator.dao.RelationalMapDao;
import com.ebts.generator.dao.TopSearchTableDao;
import com.ebts.generator.dao.UniQueryDao;
import com.ebts.generator.dto.TopResult;
import com.ebts.generator.entity.*;
import com.ebts.generator.service.TopQueryService;
import com.github.pagehelper.PageHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author: Clay
 * @date: 2021/7/23 21:51
 * @description：TopQueryServiceImpl
 * @email: clay@hchyun.com
 */
@Service
public class TopQueryServiceImpl implements TopQueryService {
    private final Logger logger = LoggerFactory.getLogger(TopQueryServiceImpl.class);

    @Autowired
    private TopSearchTableDao topSearchTableDao;
    @Autowired
    private RelationalMapDao relationalMapDao;
    @Autowired
    private QueryDao queryDao;
    @Autowired
    private UniQueryDao uniQueryDao;

    @Override
    public Map<String, Object> selectTopSearchTables() {
        Map<String, Object> modeMap = new HashMap<>();
        //采用原子式获取到forEach中的值,原子式能够允许其他线程进行尝试性的修改值
        AtomicReference<String> roleIds = new AtomicReference<>("");
        SecurityUtils.getLoginUser().getUser().getRoles().forEach(role->{
            roleIds.updateAndGet(v -> v + role.getRoleId()+",");
        });
        modeMap.put("tables", topSearchTableDao.selectTopSearchTables(roleIds.get().substring(0,roleIds.get().length()-1).split(",")));
        modeMap.put("relationalMap", relationalMapDao.selectRelationalMapList(new RelationalMap()));
        return modeMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ServerResult<TopResult> preview(String jsonData, String id) {
        try {

            JSONObject graphData = JSON.parseObject(jsonData);
            //将数据格式化为自定义的java类列表
            List<TopEdge> topEdges = JSON.parseArray(graphData.getString("edges"), TopEdge.class);
            List<TopNode> topNodes = JSON.parseArray(graphData.getString("nodes"), TopNode.class);
            if (topNodes.size() == 0) {
                return new ServerResult<>(false, "top图结构有误,请整理后在试");
            }
            //整理连线的关系
            StringBuffer sql = new StringBuffer("");
            StringBuilder selectSql = new StringBuilder("select ");
            StringBuilder selectQuery = new StringBuilder(" where 1=1 ");
            TopNode mainNode = null;
            if (topNodes.size() > 1) {
                for (int i = 0; i < topEdges.size(); i++) {
                    for (int j = 0; j < topEdges.size(); j++) {
                        if (i != j) {
                            TopEdge topEdgeJ = topEdges.get(j);
                            if (topEdges.get(i).getSource().equals(topEdgeJ.getTarget())) {
                                topEdges.get(i).setSourceEdge(topEdgeJ.getId());
                            }
                        }
                    }
                }
                // 解析出top图中与主表的连线,可能为多条
                List<TopEdge> targets = new ArrayList<>();
                for (int i = 0; i < topEdges.size(); i++) {
                    if (topEdges.get(i).getSourceEdge() == null) {
                        targets.add(topEdges.get(i));
                    }
                }

                mainNode = getNode(topNodes, targets.get(0).getSource());
                // 组合查询字段信息
                ParseSelectSQL(mainNode, topNodes, selectSql, selectQuery);

                // 组合表与表之间得到关联关系sql
                String relSql = "";
                for (int i = 0; i < targets.size(); i++) {
                    relSql += getRelSql(topNodes, topEdges, targets.get(i));
                }


                sql.append(selectSql).append(" from ").append(mainNode.getTable()).append(" ")
                        .append(mainNode.getRelTable()).append(relSql);

            } else {
                mainNode = topNodes.get(0);
                selectSql.append(mainNode.getTableSQL());
                selectQuery.append(mainNode.getQuerySql());
                sql.append(selectSql).append(" from ").append(mainNode.getTable()).append(" ")
                        .append(mainNode.getRelTable());
            }
            UniQuery uniQuery = new UniQuery();
            uniQuery.setId(Long.valueOf(id));
            uniQuery.setUqSql(sql.toString());
            uniQuery.setIsPreview(1);
            List<UniCon> uniCons = uniQuery.getUniCons();
            for (TopNode node : topNodes) {
                JSONArray columns = node.getColumns();
                for (int i = 0; i < columns.size(); i++) {
                    if (columns.getJSONObject(i).getInteger("isQuery") == 1) {
                        JSONObject column = columns.getJSONObject(i);
                        UniCon uniCon = new UniCon(uniQuery.getId(), column.getString("ucName"), node.getRelTable() + "." + column.getString("columnName"), column.getString("ucCon"), column.getString("ucType"), column.getInteger("type"));
                        uniCons.add(uniCon);
                    }
                }
            }

            int query = uniQueryDao.updateUniQuery(uniQuery);
            if (query > 0) {
                if (uniQuery.getUniCons() != null && uniQuery.getUniCons().size() > 0) {
                    queryDao.deleteUniCon(uniQuery.getId());
                    queryDao.insertUniCon(uniCons);
                }
            }


            sql.append(selectQuery);
            PageHelper.startPage(1, 10, "");
            List<Map<String, Object>> dataMap = queryDao.UniQuery(sql.toString());
            TopResult topResult = new TopResult(dataMap,uniCons);
            return new ServerResult<>(true, topResult);
        } catch (RuntimeException e) {
            e.printStackTrace();
            logger.error(e.getMessage());
            return new ServerResult<>(false, e.getMessage());
        }
    }

    @Override
    public ServerResult<String> online(Long id) {
        return null;
    }

    void ParseSelectSQL(TopNode mainNode, List<TopNode> topNodes, StringBuilder selectSql, StringBuilder selectQuery) {
        selectSql.append(mainNode.getTableSQL());
        selectQuery.append(mainNode.getQuerySql());
        for (TopNode node : topNodes) {
            if (node.getId().equals(mainNode.getId())) {
                continue;
            }
            if (node.getTableSQL().trim().equals("")){
                continue;
            }
            selectSql.append(", ").append(node.getTableSQL());
            selectQuery.append(node.getQuerySql());
        }
    }


    TopNode getNode(List<TopNode> nodes, String id) {
        for (int i = 0; i < nodes.size(); i++) {
            if (nodes.get(i).getId().equals(id)) {
                return nodes.get(i);
            }
        }
        return null;
    }

    List<TopEdge> getEdgeListById(List<TopEdge> edges, String id) {
        List<TopEdge> targetEdge = new ArrayList<>();
        for (TopEdge edge : edges) {
            if (edge.getSource().equals(id)) {
                targetEdge.add(edge);
            }
        }
        return targetEdge;
    }

    String getRelSql(List<TopNode> nodes, List<TopEdge> edges, TopEdge edge) {
        int i = 0;
        // 定义sql 变量
        StringBuffer sql = new StringBuffer("");
        List<TopEdge> targetEdge;
        while (true) {
            // 获取到连线的两个表
            TopNode source = getNode(nodes, edge.getSource());
            TopNode target = getNode(nodes, edge.getTarget());
            //进行sql拼接
            sql.append(" ").append(edge.getAppConfig().getString("associated")).append(" join ")
                    .append(target.getTable()).append(" ").append(target.getRelTable()).append(" on ")
                    .append(target.getRelTable()).append(".").append(edge.getAppConfig()
                    .getString("relComment")).append(" = ").append(source.getRelTable())
                    .append(".").append(edge.getAppConfig().getString("tableComment"));
            // 获取到target 连接表的连线
            targetEdge = getEdgeListById(edges, edge.getTarget());
            // 判断连线条数
            if (targetEdge.size() == 1) {
                edge = targetEdge.get(0);
            } else if (targetEdge.size() > 1) {
                //多条则进行递归解析
                while (i < targetEdge.size()) {
                    sql.append(getRelSql(nodes, edges, targetEdge.get(i)));
                    i++;
                }
                break;
            } else {
                //没有则退出循环返回解析好的sql
                break;
            }
        }
        return sql.toString();
    }
}
