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

import com.google.common.collect.Lists;
import com.liangjian.dataplatform.member.constant.DataNodeConstant;
import com.liangjian.dataplatform.member.constant.MetaCoreConstant;
import com.liangjian.dataplatform.member.dao.primary.MetaCoreRepository;
import com.liangjian.dataplatform.member.dao.primary.MetaRelRepository;
import com.liangjian.dataplatform.member.entity.po.primary.MetaCore;
import com.liangjian.dataplatform.member.entity.vo.MetaRelVo;
import com.liangjian.dataplatform.member.entity.vo.datanodes.*;
import com.liangjian.dataplatform.member.entity.vo.detailvo.*;
import com.liangjian.dataplatform.member.service.DataNodeTransferService;
import com.liangjian.dataplatform.member.service.IMetaCoreQueryService;
import com.liangjian.dataplatform.member.service.IMetaReverseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Author: 黄子奇
 * @Date: 2019/8/26 14:12
 */
@Service
@Transactional
public class MetaCoreQueryService implements IMetaCoreQueryService {

    private Function<MetaCore, DataNode> transferFunc;


    @Autowired
    private MetaCoreRepository metaCoreRepository;
    @Autowired
    private DataNodeTransferService dataNodeTransferService;
    @Autowired
    private MetaDataSourceTransfer metaDataSourceTransfer;
    @Autowired
    private IMetaReverseService iMetaReverseService;
    @Autowired
    private MetaRelRepository metaRelRepository;

    @PostConstruct
    private void initFunc() {
        this.transferFunc = i -> dataNodeTransferService.transfer(i);
    }

    @Override
    public List<DataNode> createRootNodes() {
        MetaCoreRoot pdm = new MetaCoreRoot(MetaCoreConstant.ROOT_TYPE_PDM,"模型",MetaCoreConstant.ROOT_TYPE_PDM);
        pdm.setNamespace(MetaCoreConstant.ROOT_TYPE_PDM);
        MetaCoreRoot itf = new MetaCoreRoot(MetaCoreConstant.ROOT_TYPE_ITF,"接口",MetaCoreConstant.ROOT_TYPE_ITF);
        itf.setNamespace(MetaCoreConstant.ROOT_TYPE_ITF);
        MetaCoreRoot db = new MetaCoreRoot(MetaCoreConstant.ROOT_TYPE_DB,"落地库",MetaCoreConstant.ROOT_TYPE_DB);
        db.setNamespace(MetaCoreConstant.ROOT_TYPE_DB);
        MetaCoreRoot host = new MetaCoreRoot(MetaCoreConstant.ROOT_TYPE_HOST,"文本数据",MetaCoreConstant.ROOT_TYPE_HOST);
        host.setNamespace(MetaCoreConstant.ROOT_TYPE_HOST);
        return Lists.newArrayList(pdm,itf,db,host);
    }

    /*
    每次获取节点数据时，需要做两步判断：
    一：判断id是否通过 . 分割，若有，说明是需要从反向工程去获取
    二：获取到父级节点后判断nodeType，若是phy.Database，需要从反向工程去获取下级节点
     */

    private List<DataNode> judgeAndGetChildren(String pid) {
        String[] pids = pid.split("\\.");
        if(pids.length > 1) {
            MetaCore metaCore = metaCoreRepository.getOne(pids[0]);

            if(pid.endsWith(DataNodeConstant.VIRTUAL_SUFFIX_TABLE)) {
                //tables结尾
                pid = pid.replace(DataNodeConstant.VIRTUAL_SUFFIX_TABLE,"");
                String schema = pid.split("\\.")[1];
                MetaDataSource mds = (MetaDataSource) metaDataSourceTransfer.transfer(metaCore);
                MetaTable metaTable = new MetaTable(pid,mds.getId()+"."+schema, mds.getBizDomain(),mds.getParent(),mds.getComment(),mds.getRemark(),mds.getCrtDate(),mds.getLastUpdate(),mds.getCrtUser(),mds.getNamespace(),mds.getType(),schema,mds.getEntry());
                return iMetaReverseService.getTables(metaTable);
            } else if(pid.endsWith(DataNodeConstant.VIRTUAL_SUFFIX_VIEW)){
                //views结尾
                pid = pid.replace(DataNodeConstant.VIRTUAL_SUFFIX_VIEW,"");
                String schema = pid.split("\\.")[1];
                MetaDataSource mds = (MetaDataSource) metaDataSourceTransfer.transfer(metaCore);
                MetaView metaView = new MetaView(pid,mds.getId()+"."+schema, mds.getBizDomain(),mds.getParent(),mds.getComment(),mds.getRemark(),mds.getCrtDate(),mds.getLastUpdate(),mds.getCrtUser(),mds.getNamespace(),mds.getType(),schema,mds.getEntry());
                return iMetaReverseService.getViews(metaView);
            } else if(pid.endsWith(DataNodeConstant.VIRTUAL_SUFFIX_PROCEDURE)){
                //procedures结尾
                pid = pid.replace(DataNodeConstant.VIRTUAL_SUFFIX_PROCEDURE,"");
                String schema = pid.split("\\.")[1];
                MetaDataSource mds = (MetaDataSource) metaDataSourceTransfer.transfer(metaCore);
                MetaProcedure metaProcedure = new MetaProcedure(pid,mds.getId()+"."+schema, mds.getBizDomain(),mds.getParent(),mds.getComment(),mds.getRemark(),mds.getCrtDate(),mds.getLastUpdate(),mds.getNamespace(),mds.getCrtUser(),mds.getType(),schema,mds.getEntry());
                return iMetaReverseService.getProduces(metaProcedure);
            } else if(pids.length > 2) {
                //在表、视图、存储过程目录更下一级的，已经是表、视图、存储过程了
                MetaDataSource mds = (MetaDataSource) metaDataSourceTransfer.transfer(metaCore);
                return iMetaReverseService.getColumns(mds, pids);
            } else {
                //就是Schema的节点
                String schema = pid.split("\\.")[1];
                MetaDataSource mds = (MetaDataSource) metaDataSourceTransfer.transfer(metaCore);
                MetaSchema metaSchema = new MetaSchema(pid,mds.getId()+"."+schema, mds.getName(),mds.getBizDomain(),mds.getNodeType(),mds.getParent(),mds.getComment(),mds.getRemark(),mds.getCrtDate(),mds.getLastUpdate(),mds.getCrtUser(),mds.getNamespace(),mds.getType(),schema,mds.getEntry());
                return iMetaReverseService.getChildren(metaSchema);
            }
        } else {
            Optional<MetaCore> opt = metaCoreRepository.findById(pid);
            if(!opt.isPresent()){
                return null;
            }
            MetaCore metaCore = opt.get();
            if(metaCore.getMetaMdl().equals(MetaCoreConstant.PHY_DATASOURCE)) {
                MetaDataSource mds = (MetaDataSource) metaDataSourceTransfer.transfer(metaCore);
                try{
                    return iMetaReverseService.getSchema(mds);
                } catch (Exception ex) {
                    ex.printStackTrace();
                    return Collections.emptyList();
                }
            } else {
                //都不是，那就正常获取
                return null;
            }
        }
    }


    /*
        获取数据列表系列
     */

    @Override
    public List<DataNode> queryByPrtId(String pid) {
        List<DataNode> judge = judgeAndGetChildren(pid);

        return judge != null ? judge : metaCoreRepository.queryByPrtId(pid).stream()
                .map(transferFunc)
                .collect(Collectors.toList());
    }

    @Override
    public Page<DataNode> queryByPrtId(String pid, Integer page, Integer size) {
        List<DataNode> judge = judgeAndGetChildren(pid);
        if(judge != null) {
            //不为空的时候模拟分页
            if(judge.size() > page * size){
                judge = judge.subList((page-1)*size, page*size);
                return new PageImpl<>(judge,PageRequest.of(page,size),judge.size());
            } else if(judge.size() > (page-1) * size) {
                int start = page-2 > 0 ? page-2 : 0;
                judge = judge.subList( start, judge.size());
                return new PageImpl<>(judge,PageRequest.of(page,size),judge.size());
            } else {
                return new PageImpl<>(Collections.emptyList());
            }
        }

        Pageable pageable = PageRequest.of(page,size);
        return metaCoreRepository.queryByPrtId(pid,pageable)
                .map(transferFunc);
    }

    @Override
    public List<DataNode> queryByPrtIdAndMetaMdl(String pid, String metaMdl) {
        return metaCoreRepository.queryByPrtIdAndMetaMdl(pid,metaMdl)
                .stream()
                .map(transferFunc)
                .collect(Collectors.toList());
    }

    @Override
    public Page<DataNode> queryByPrtIdAndMetaMdl(String pid, String metaMdl, Integer page, Integer size) {
        Pageable pageable = PageRequest.of(page,size);
        return metaCoreRepository.queryByPrtIdAndMetaMdl(pid,metaMdl,pageable)
                .map(transferFunc);
    }

    @Override
    public List<DataNode> queryByRootType(String rootType) {
        return metaCoreRepository.queryByPrtId(rootType)
                .stream()
                .map(transferFunc)
                .collect(Collectors.toList());
    }

    @Override
    public Page<DataNode> queryByRootType(String rootType, Integer page, Integer size) {
        Pageable pageable = PageRequest.of(page,size);
        return metaCoreRepository.queryByPrtId(rootType,pageable)
                .map(transferFunc);
    }

    /*
    * 获取数据详细信息系列
    * */


    /*
    每次获取节点数据时，同样需要做一步判断：
    一：判断id是否通过 . 分割，若有，说明是需要从反向工程去获取
     */
    private DetailVo judgeAndGet(String pid) {
        String[] pids = pid.split("\\.");
        if(pids.length > 1) {
            MetaCore metaCore = metaCoreRepository.getOne(pids[0]);

            if(pid.endsWith(DataNodeConstant.VIRTUAL_SUFFIX_TABLE)) {
                //tables结尾
                String schema = pid.split("\\.")[1];
                MetaDataSource mds = (MetaDataSource) metaDataSourceTransfer.transfer(metaCore);
                MetaTable metaTable = new MetaTable(pid,mds.getId()+"."+schema,mds.getBizDomain(),mds.getParent(),mds.getComment(),mds.getRemark(),mds.getCrtDate(),mds.getLastUpdate(),mds.getCrtUser(),mds.getNamespace(),mds.getType(),schema,mds.getEntry());
                return getPackageDetail(metaTable);
            } else if(pid.endsWith(DataNodeConstant.VIRTUAL_SUFFIX_VIEW)){
                //views结尾
                String schema = pid.split("\\.")[1];
                MetaDataSource mds = (MetaDataSource) metaDataSourceTransfer.transfer(metaCore);
                MetaView metaView = new MetaView(pid,mds.getId()+"."+schema, mds.getBizDomain(),mds.getParent(),mds.getComment(),mds.getRemark(),mds.getCrtDate(),mds.getLastUpdate(),mds.getCrtUser(),mds.getNamespace(),mds.getType(),schema,mds.getEntry());
                return getPackageDetail(metaView);
            } else if(pid.endsWith(DataNodeConstant.VIRTUAL_SUFFIX_PROCEDURE)){
                //procedures结尾
                String schema = pid.split("\\.")[1];
                MetaDataSource mds = (MetaDataSource) metaDataSourceTransfer.transfer(metaCore);
                MetaProcedure metaProcedure = new MetaProcedure(pid,mds.getId()+"."+schema, mds.getBizDomain(),mds.getParent(),mds.getComment(),mds.getRemark(),mds.getCrtDate(),mds.getLastUpdate(),mds.getNamespace(),mds.getCrtUser(),mds.getType(),schema,mds.getEntry());
                return getPackageDetail(metaProcedure);
            } else if(pids.length == 5){
                //在表、视图下的字段了
                MetaDataSource mds = (MetaDataSource) metaDataSourceTransfer.transfer(metaCore);
                return getPDMColumnDetail(mds,pids);
            } else if(pids.length > 2) {
                //在表、视图、存储过程目录更下一级的，已经是表、视图、存储过程了
                MetaDataSource mds = (MetaDataSource) metaDataSourceTransfer.transfer(metaCore);
                return getPDMDetail(mds,pids);
            } else {
                //就是Schema的节点
                String schema = pid.split("\\.")[1];
                MetaDataSource mds = (MetaDataSource) metaDataSourceTransfer.transfer(metaCore);
                MetaSchema metaSchema = new MetaSchema(pid,mds.getId()+"."+schema, mds.getName(),mds.getBizDomain(),mds.getNodeType(),mds.getParent(),mds.getComment(),mds.getRemark(),mds.getCrtDate(),mds.getLastUpdate(),mds.getCrtUser(),mds.getNamespace(),mds.getType(),schema,mds.getEntry());
                return getPackageDetail(metaSchema);
            }
        } else {
            return null;
        }
    }

    private MetaCorePackage createEmptyPkg(String dataKind) {
        return new MetaCorePackage() {
            private static final long serialVersionUID = 3456208984986592579L;

            @Override
            public String getDataKind() {
                return dataKind;
            }
        };
    }

    @Override
    public DetailVo getNodeDetail(String id) {
        DetailVo vo = judgeAndGet(id);
        if(vo != null) {
            return vo;
        }

        DataNode node = null;
        List<String> rootIds = Arrays.asList("PDM","ITF","DB","HOST");
        if(rootIds.contains(id)){
            node = createRootNodes().get(rootIds.indexOf(id));
        } else {
            MetaCore metaCore = metaCoreRepository.getOne(id);
            node = transferFunc.apply(metaCore);
        }

        switch (node.getDataKind()) {
            case DataNodeConstant.KIND_ROOT : return getRootDetailVo(node);
            case DataNodeConstant.KIND_PACKAGE : return getPackageDetail(node);
            case DataNodeConstant.KIND_DATASOURCE: return getDataSourceDetail(node);
            case DataNodeConstant.KIND_PDM: return getPDMDetail(node,null);
            case DataNodeConstant.KIND_PDMCLM: return getPDMColumnDetail(node, null);
            default: return new DetailVo() {
                private static final long serialVersionUID = 2714816293480094685L;
                @Override
                public DataNode getRealNode() {
                    return null;
                }

                @Override
                public void setRealNode(Object o) {

                }

                @Override
                public String getNodeType() {
                    return "unknown";
                }

                @Override
                public String getDataNodeType() {
                    return "unknown";
                }
            };
        }
    }

    @Override
    public PackageDetailVo getPackageDetail(DataNode node) {
        //Schema虚节点
        if(node.getNodeType().equals(MetaCoreConstant.RELATION_SCHEMA)){
            Map<String,String> children = new LinkedHashMap<>();
            for (DataNode dataNode : queryByPrtId(node.getId())) {
                Map<String,String> kv = queryByPrtId(dataNode.getId()).stream()
                        .collect(Collectors.toMap(
                                DataNode::getId,
                                DataNode::getName
                        ));
                children.putAll(kv);
            }
            MetaCorePackage pkg = new MetaCorePackage();
            pkg.setNodeType(node.getNodeType());
            pkg.setId(node.getId());
            pkg.setName(node.getName());
            return new PackageDetailVo(pkg,children);
        }
        //虚节点
        if(node.getNodeType().equals(MetaCoreConstant.RELATION_TABLES) ||
                node.getNodeType().equals(MetaCoreConstant.RELATION_VIEWS) ||
                node.getNodeType().equals(MetaCoreConstant.RELATION_PROCEDURES)) {
            MetaCorePackage pkg = createEmptyPkg(node.getDataKind());
            pkg.setNodeType(node.getNodeType());
            return new PackageDetailVo(pkg,null);
        }

        //普通目录
        Map<String,String> children = queryByPrtId(node.getId())
                .stream()
                .collect(Collectors.toMap(i -> i.getId(),i -> i.getName()));
        return new PackageDetailVo((MetaCorePackage) node,children);
    }

    @Override
    public DataSourceDetailVo getDataSourceDetail(DataNode node) {
        return new DataSourceDetailVo((MetaDataSource) node);
    }

    @Override
    public PDMDetailVo getPDMDetail(DataNode node, Object extract) {
        if(extract == null) {
            //返回普通的
            List<PDMColumn> children = queryByPrtId(node.getId()).stream().map(i -> (PDMColumn) i).collect(Collectors.toList());
            List<MetaRelVo> relations = metaRelRepository.queryBySrc(node.getId())
                    .stream()
                    .map(MetaRelVo::fromMetaRel)
                    .collect(Collectors.toList());
            PDMDetailVo detailVo = new PDMDetailVo((PDM) node,children,relations);
            return detailVo;
        } else {
            //反向工程获取
            String[] pids = (String[]) extract;
            String id = Stream.of(pids).reduce((i,j) -> i+"."+j).get();
            List<PDMColumn> children = queryByPrtId(id).stream().map(i -> (PDMColumn)i).collect(Collectors.toList());
            MetaDataSource mds = (MetaDataSource) node;
            PDM pdm = new PDM(
                    id,
                    pids[0]+"."+pids[1],
                    pids[pids.length-1],
                    mds.getBizDomain(),
                    MetaCoreConstant.RELATION_TABLE,
                    true,
                    "",    //不知道反向工程获取的表的注释
                    "",      //反向工程获取的表备注
                    null,    //不知道反向工程获取的表的创建时间
                    null, //
                    "",      //
                    mds.getNamespace(),
                    pids[pids.length-1]
            );
            PDMDetailVo detailVo = new PDMDetailVo(pdm,children,null);
            return detailVo;
        }
    }

    @Override
    public PDMColumnDetailVo getPDMColumnDetail(DataNode node, Object extract) {
        if(extract == null) {
            return new PDMColumnDetailVo((PDMColumn) node);
        } else {
            //反向工程获取
            String[] pids = (String[]) extract;
            String colName = pids[pids.length-1];
            List<DataNode> columns = iMetaReverseService.getColumns((MetaDataSource) node, pids);
            PDMColumn column = (PDMColumn) columns.stream().filter(i -> i.getName().equals(colName)).findFirst().orElse(null);
            PDMColumnDetailVo vo = new PDMColumnDetailVo(column);
            return vo;
        }
    }

    @Override
    public RootDetailVo getRootDetailVo(DataNode node) {
        return new RootDetailVo((MetaCoreRoot) node);
    }

    @Override
    public List<DataNode> getRelaChildTables(String id) {
        DetailVo pdmDetail = getNodeDetail(id);
        if(!(pdmDetail instanceof PDMDetailVo)){
            return Collections.emptyList();
        }

        PDMDetailVo vo = (PDMDetailVo) pdmDetail;
        List<MetaRelVo> relVos = vo.getRelations();
        List<String> relIds = relVos.stream().map(i -> i.getDest()).collect(Collectors.toList());
        List<DataNode> relPdms = metaCoreRepository.queryByIdIn(relIds).stream()
                .map(transferFunc)
                .collect(Collectors.toList());
        return relPdms;
    }
}
