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.entity.database.EtlDataBaseFactory;
import com.liangjian.dataplatform.member.entity.database.EtlDataBaseInterface;
import com.liangjian.dataplatform.member.entity.etl.EtlManDbResourceVo;
import com.liangjian.dataplatform.member.entity.po.primary.MetaCore;
import com.liangjian.dataplatform.member.entity.pojo.DataBaseInfo;
import com.liangjian.dataplatform.member.entity.pojo.PkAndNullable;
import com.liangjian.dataplatform.member.entity.vo.datanodes.*;
import com.liangjian.dataplatform.member.service.IMetaReverseService;
import com.liangjian.dataplatform.member.util.hadoop.HiveKbsUitls;
import org.pentaho.di.core.database.DatabaseMeta;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Author: 黄子奇
 * @Date: 2019/8/27 11:24
 */
@Service
public class MetaReverseService implements IMetaReverseService {

    @Autowired
    private EtlDataBaseFactory etlDataBaseFactory;

    @Override
    public List<DataNode> getSchema(MetaDataSource mds) throws Exception {

        List<String> schemas = queryDsSchemas(mds.getType(), mds.getEntry());
        return schemas == null ?
                Collections.emptyList() :
                schemas.stream().map(schema -> {
                    MetaSchema metaSchema = new MetaSchema(
                            mds.getId() + "." + schema,
                            mds.getId(),
                            schema,
                            mds.getBizDomain(),
                            MetaCoreConstant.RELATION_SCHEMA,
                            true,
                            mds.getComment(),
                            mds.getRemark(),
                            mds.getCrtDate(),
                            mds.getLastUpdate(),
                            mds.getCrtUser(),
                            mds.getNamespace(),
                            mds.getType(),
                            schema,
                            mds.getEntry()
                    );
                    return metaSchema;
                }).collect(Collectors.toList());
    }

    @Override
    public List<DataNode> getChildren(MetaSchema metaSchema) {
        //给Id添加后缀，防止认不出来
        MetaTable metaTable = new MetaTable(metaSchema.getId() + DataNodeConstant.VIRTUAL_SUFFIX_TABLE,
                metaSchema.getPrtId(),
                metaSchema.getBizDomain(), true,
                metaSchema.getComment(),
                metaSchema.getRemark(),
                metaSchema.getCrtDate(),
                metaSchema.getLastUpdate(),
                metaSchema.getCrtUser(),
                metaSchema.getNamespace(),
                metaSchema.getType(),
                metaSchema.getSchema(),
                metaSchema.getInfo());
        MetaView metaView = new MetaView(metaSchema.getId() + DataNodeConstant.VIRTUAL_SUFFIX_VIEW,
                metaSchema.getPrtId(),
                metaSchema.getBizDomain(), true,
                metaSchema.getComment(),
                metaSchema.getRemark(),
                metaSchema.getCrtDate(),
                metaSchema.getLastUpdate(),
                metaSchema.getCrtUser(),
                metaSchema.getNamespace(),
                metaSchema.getType(),
                metaSchema.getSchema(),
                metaSchema.getInfo());
        MetaProcedure procedure = new MetaProcedure(metaSchema.getId() + DataNodeConstant.VIRTUAL_SUFFIX_PROCEDURE,
                metaSchema.getPrtId(),
                metaSchema.getBizDomain(), true,
                metaSchema.getComment(),
                metaSchema.getRemark(),
                metaSchema.getCrtDate(),
                metaSchema.getLastUpdate(),
                metaSchema.getCrtUser(),
                metaSchema.getNamespace(),
                metaSchema.getType(),
                metaSchema.getSchema(),
                metaSchema.getInfo());
        return Lists.newArrayList(metaTable, metaView, procedure);
    }

    @Override
    public List<DataNode> getTables(MetaTable metaTable) {
        if (metaTable.getId().contains("hive")) {
            String dsId = metaTable.getId();
            return getHiveTables(dsId.split("\\|")[0], dsId.split("\\|")[1], dsId.split("\\|")[2], "");
        }

        EtlManDbResourceVo dbvo = getEtlManDbResourceVo(metaTable.getType(), metaTable.getInfo());
        EtlDataBaseInterface database = null;
        try {
            database = etlDataBaseFactory.getDatabase(dbvo);
            database.connect();

            String schema = metaTable.getSchema();
            if (schema.indexOf("?") > 0) {
                schema = schema.substring(0, schema.indexOf("?"));
            }

            String[] tables = database.getTablenames(schema, null, true);
            List<DataNode> pdms = Stream.of(tables).map(tableName -> {
                String[] tableNameArray = tableName.split("\\.");
                String id = metaTable.getId() + "." + tableName;
                String name = tableName;
                String fileName = tableName;
                if (tableNameArray.length == 2) {
                    name = tableNameArray[1];
                    fileName = tableNameArray[1];
                    //id = id + "."+tableNameArray[1];
                }

                PDM pdm = new PDM(
                        id,
                        metaTable.getId(),
                        name,
                        metaTable.getBizDomain(),
                        MetaCoreConstant.RELATION_TABLE,
                        true,
                        metaTable.getComment(),
                        metaTable.getRemark(),
                        metaTable.getCrtDate(),
                        metaTable.getLastUpdate(),
                        metaTable.getCrtUser(),
                        name,
                        fileName
                );
                return pdm;
            }).collect(Collectors.toList());

            return pdms;
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if (database != null) {
                database.disconnect();
            }
        }
        return null;
    }

    @Override
    public List<DataNode> getViews(MetaView metaView) {
        if (metaView.getId().contains("hive")) {
            //TODO 获取hive表
        }

        EtlManDbResourceVo dbvo = getEtlManDbResourceVo(metaView.getType(), metaView.getInfo());
        EtlDataBaseInterface database = null;
        try {
            database = etlDataBaseFactory.getDatabase(dbvo);
            database.connect();

            String schema = metaView.getSchema();
            if (schema.indexOf("?") > 0) {
                schema = schema.substring(0, schema.indexOf("?"));
            }

            String[] tables = database.getViews(schema, null, true);
            List<DataNode> pdms = Stream.of(tables).map(tableName -> {
                String[] tableNameArray = tableName.split("\\.");
                String id = metaView.getId() + "." + tableName;
                String name = tableName;
                String fileName = tableName;
                if (tableNameArray.length == 2) {
                    name = tableNameArray[1];
                    fileName = tableNameArray[1];
                    //id = id + "."+tableNameArray[1];
                }

                PDM pdm = new PDM(
                        id,
                        metaView.getId(),
                        name,
                        metaView.getBizDomain(),
                        MetaCoreConstant.RELATION_VIEW,
                        true,
                        metaView.getComment(),
                        metaView.getRemark(),
                        metaView.getCrtDate(),
                        metaView.getLastUpdate(),
                        metaView.getCrtUser(),
                        name,
                        fileName
                );
                return pdm;
            }).collect(Collectors.toList());

            return pdms;
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if (database != null) {
                database.disconnect();
            }
        }
        return null;
    }

    @Override
    public List<DataNode> getProduces(MetaProcedure metaProcedure) {
        //TODO
        return Collections.emptyList();
    }

    private List<String> queryDsSchemas(String type, DataBaseInfo baseInfo) throws Exception {
        EtlManDbResourceVo dbvo = getEtlManDbResourceVo(type, baseInfo);

        EtlDataBaseInterface database = null;
        try {
            database = etlDataBaseFactory.getDatabase(dbvo);
            database.connect();

            List<String> schemaList = new ArrayList<>();
            if (type.equals("Mysql")) {
                schemaList.add(baseInfo.getServerName());
            } else if (type.equals("Oracle")) {
                schemaList.add(baseInfo.getUser());
            } else {
                String serverName = baseInfo.getServerName();
                if (serverName.indexOf(";") > 0) {
                    serverName = serverName.substring(0, serverName.indexOf(";"));
                }
                schemaList.add(serverName);
            }

            return schemaList;
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if (database != null) {
                database.disconnect();
            }
        }
        return null;
    }

    private EtlManDbResourceVo getEtlManDbResourceVo(String type, DataBaseInfo baseInfo) {
        EtlManDbResourceVo dbvo = new EtlManDbResourceVo();
        dbvo.setDbType(type);
        dbvo.setDbConType(DatabaseMeta.TYPE_ACCESS_NATIVE + "");
        dbvo.setDbHost(baseInfo.getHost());
        dbvo.setDbName(baseInfo.getServerName());
        dbvo.setDbPort(baseInfo.getPort());
        dbvo.setDbUser(baseInfo.getUser());
        dbvo.setDbPassword(baseInfo.getPwd());
        dbvo.setKrb5ConfigPath(baseInfo.getKrb5ConfigPath());
        dbvo.setHadoopHomeDir(baseInfo.getHadoopHomeDir());
        dbvo.setKeytabPath(baseInfo.getKeytabPath());
        dbvo.setPrincipal(baseInfo.getPrincipal());

        if (type != null && type.equalsIgnoreCase("mssqlnative"))
            dbvo.setDbParameter("MSSQLUseIntegratedSecurity=false;");//encrypt=false;trustServerCertificate=true;create=true
        if (type != null && type.equalsIgnoreCase("mysql"))
            dbvo.setDbParameter("useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC;");
        return dbvo;
    }


    //这个逻辑基本没有改，是使用原来的，但是好像逻辑有问题。
    private List<DataNode> getHiveTables(String database, String role, String privilege, String databaseId) {
        List<MetaCore> tabList = HiveKbsUitls.getTables(database, role, privilege, databaseId);

        return tabList.stream().map(tab -> {
            PDM pdm = new PDM(
                    tab.getBizDomain(),
                    tab.getPrtId(),
                    tab.getName(),
                    tab.getBizDomain(),
                    MetaCoreConstant.RELATION_VIEW,
                    true,
                    tab.getCmt(),
                    tab.getRemarks(),
                    tab.getCrtDate(),
                    tab.getLastUpdate(),
                    tab.getCrtUser(),
                    tab.getChName(),
                    tab.getFileName()
            );
            return pdm;
        }).collect(Collectors.toList());
    }

    @Override
    public List<DataNode> getColumns(MetaDataSource mds, String[] params) {
        if (params.length == 5) {
            //这种情况是子段信息了
            return Collections.emptyList();
        }
        String dbName = params[1];
        String schema = params[2];
        String modelName = params[3]; //源代码是就是modelName，是tableName的意思

        EtlManDbResourceVo dbvo = getEtlManDbResourceVo(mds.getType(), mds.getEntry());

        EtlDataBaseInterface database = null;
        try {
            database = etlDataBaseFactory.getDatabase(dbvo);
            database.connect();

            if (schema.indexOf("?") > 0) {
                schema = schema.substring(0, schema.indexOf("?"));
            }

            DatabaseMetaData dbmd = database.getConnection().getMetaData();

            if ("Oracle".equals(mds.getType())) {
                schema = schema.toUpperCase();
            }

            ResultSet rs = dbmd.getColumns(null, schema, modelName, null);

            ResultSet pkRs = dbmd.getPrimaryKeys(null, schema, modelName);

            //主键字段
            List<String> pkColumns = new ArrayList<>();
            while (pkRs.next()) {
                pkColumns.add(pkRs.getString("COLUMN_NAME"));
            }

            //外键, 好像这里不太需要
//            List<Key> keys = new ArrayList<>();
//            ResultSet impRs = dbmd.getImportedKeys(null, schema, modelName);
//            while(impRs.next()){
//                Key key = new Key();
//                key.setName(impRs.getString("FK_NAME"));
//
//                key.setFkClm(impRs.getString("FKCOLUMN_NAME"));
//                key.setRefSchema(impRs.getString("PKTABLE_CAT"));
//                key.setRefTable(impRs.getString("PKTABLE_NAME"));
//                key.setRefClms(impRs.getString("PKCOLUMN_NAME"));
//                key.setUpdateRule(impRs.getString("UPDATE_RULE"));
//                key.setDeleteRule(impRs.getString("DELETE_RULE"));
//
//                key.setType("外键");
//                keys.add(key);
//            }

            //索引，好像这里页不太需要
//            List<Index> indexs = new ArrayList<>();
//            if("HIVE2KBS".equals(mds.getType()) || "HIVE2".equals(mds.getType())){
//
//            }else{
//                ResultSet idxRs = dbmd.getIndexInfo(null, schema, modelName, false, false);
//                while(idxRs.next()){
//                    Index idx = new Index();
//                    idx.setName(idxRs.getString("INDEX_NAME"));
//                    idx.setClms(idxRs.getString("COLUMN_NAME"));
//                    idx.setType(idxRs.getString("NON_UNIQUE"));
//                    idx.setIdxFun(idxRs.getString("TYPE"));
//
//                    indexs.add(idx);
//                }
//            }

            //字段，原来的代码
//            List<Column> columns = new ArrayList<>();
//            int nullable;
//            while(rs.next()){
//                Column column = new Column();
//                column.setName(rs.getString("COLUMN_NAME"));
//                column.setType(rs.getString("TYPE_NAME"));
//                column.setLength(rs.getInt("COLUMN_SIZE"));
//                column.setPrecision(rs.getInt("DECIMAL_DIGITS"));
//                column.setChName(rs.getString("REMARKS")==null?"":rs.getString("REMARKS"));
//                column.setCmt(rs.getString("REMARKS")==null?"":rs.getString("REMARKS"));
//
//                nullable = rs.getInt("NULLABLE");
//                if(nullable == 0){
//                    column.setNullable(false);
//                }else{
//                    column.setNullable(true);
//                }
//                if(pkColumns.contains(column.getName())){
//                    column.setPk(true);
//                }
//
//                columns.add(column);
//            }
            //字段
            List<DataNode> columns = new ArrayList<>();
            //出现有获取到重复字段的问题，这里做去重
            List<String> apearNames = new ArrayList<>();
            while (rs.next()) {
                PDMColumn column = new PDMColumn();
                String columnName = rs.getString("COLUMN_NAME");
                //判断去重
                if (apearNames.contains(columnName)) {
                    continue;
                } else {
                    apearNames.add(columnName);
                }
                
                column.setName(columnName);
                column.setType(rs.getString("TYPE_NAME"));
                column.setLength(rs.getInt("COLUMN_SIZE"));
                column.setPrec(rs.getInt("DECIMAL_DIGITS"));
                column.setCnName(rs.getString("REMARKS") == null ? "" : rs.getString("REMARKS"));
                column.setComment(rs.getString("REMARKS") == null ? "" : rs.getString("REMARKS"));

                boolean nullable = rs.getInt("NULLABLE") != 0;
                boolean pk = pkColumns.contains(column.getName());
                PkAndNullable pkAndNullable = new PkAndNullable(pk, nullable);
                column.setEntry(pkAndNullable);

                column.setId(mds.getId() + "." + dbName + "." + schema + "." + modelName + "." + column.getName());
                column.setPrtId(mds.getId() + "." + dbName + "." + schema + "." + modelName);
                column.setBizDomain(mds.getBizDomain());
                column.setNodeType(MetaCoreConstant.RELATION_COLUMN);
                column.setParent(false);
                column.setCrtDate(mds.getCrtDate());
                column.setLastUpdate(mds.getLastUpdate());
                column.setCrtUser(mds.getCrtUser());
                columns.add(column);
            }
            return columns;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (database != null) {
                database.disconnect();
            }
        }
        return null;
    }
}
