package util.getdata;

import bean.Column;
import bean.DataBase;
import bean.Schema;
import bean.Table;
import bean.View;
import com.alibaba.druid.util.StringUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import ru.yandex.clickhouse.ClickHouseConnection;
import ru.yandex.clickhouse.ClickHouseDataSource;
import ru.yandex.clickhouse.settings.ClickHouseProperties;
import util.JdbcCacheUtil;
import util.StringJoin;

public class CacheHive extends CacheData {
   public static final int SCHEMA_BATCH_SIZE = 10;
   public static final int TABLE_BATCH_SIZE = 500;
   public static final int PAGE_SIZE = 5000;
   public String preFix = null;

   public CacheHive(DataBase dataBase, CountDownLatch cdl) {
      super(dataBase, cdl);
   }

   public void run() {
      System.out.println("Hive开始缓存");

      try {
         String ipPort = this.DataBase.getIp_port();
         this.preFix = "Hive_" + ipPort.replaceAll("\\.", "").replaceAll(":", "_") + "_";
         long totalStart = System.currentTimeMillis();
         this.cacheWriteToRedis();
         long totalEnd = System.currentTimeMillis();
         System.out.println("Hive结束 用时：" + (totalEnd - totalStart) / 1000L);
      } catch (Exception e) {
         e.printStackTrace();
      } finally {
         try {
            JdbcCacheUtil.closeConnection(this.connection);
            if (this.statement != null) {
               this.statement.close();
            }
         } catch (SQLException throwables) {
            throwables.printStackTrace();
         }

         this.getCdl().countDown();
         System.out.println("Hive结束缓存");
      }

   }

   public List<Schema> cacheToMemory() throws SQLException, InterruptedException {
      this.connection = this.getConn(this.DataBase);
      this.statement = this.connection.createStatement();
      List<Schema> schemaList = this.cache(this.DataBase, this.statement);
      return schemaList;
   }

   private List<Schema> cache(DataBase dataBase, Statement statement) throws InterruptedException, SQLException {
      List<Schema> schemaList;
      try {
         schemaList = this.getSchema(statement);
      } catch (Exception e) {
         e.printStackTrace();
         Thread.sleep(50L);
         schemaList = this.getSchema(statement);
      }

      if (dataBase.getIncludeSchema() != null && dataBase.getIncludeSchema().size() > 0) {
         List<Schema> filteredSchemas = new ArrayList();

         for(Schema schema : schemaList) {
            if (dataBase.getIncludeSchema().contains(schema.getName())) {
               filteredSchemas.add(schema);
            }
         }

         schemaList = filteredSchemas;
      } else {
         schemaList = new ArrayList();
      }

      int schemaSize = schemaList.size();
      int loop = (int)Math.ceil((double)1.0F * (double)schemaSize / (double)10.0F);
      List<List<Schema>> schemaSubs = new ArrayList();

      for(int i = 0; i < loop; ++i) {
         int fromIndex = i * 10;
         int toIndex = (i + 1) * 10;
         if (fromIndex > schemaSize) {
            fromIndex = schemaSize;
         }

         if (toIndex > schemaSize) {
            toIndex = schemaSize;
         }

         List<Schema> schemaSub = schemaList.subList(fromIndex, toIndex);
         schemaSubs.add(schemaSub);
      }

      Statement finalStatement = statement;

      for(List<Schema> item : schemaSubs) {
         try {
            this.cacheSchema(finalStatement, item);
         } catch (SQLException throwables) {
            throwables.printStackTrace();

            try {
               Thread.sleep(50L);
               this.cacheSchema(finalStatement, item);
            } catch (Exception e) {
               e.printStackTrace();
            }
         }
      }

      return schemaList;
   }

   private void printColumnSize(List<Schema> schemaList) {
      int i = 0;

      for(Schema schema : schemaList) {
         for(Table table : schema.getTableList()) {
            i += table.getColumnList().size();
         }
      }

      System.out.println(i);
   }

   public String getSchemaTableId(String schemaName, String tableName) {
      return schemaName + "--->" + tableName;
   }

   private void cacheSchema(Statement statement, List<Schema> schemaSub) throws SQLException {
      List<Table> tableList = new ArrayList();
      List<Column> columns = new ArrayList();
      long start1 = System.currentTimeMillis();
      List<Table> tableListTemp = this.getTables(statement, schemaSub);
      tableList.addAll(tableListTemp);
      int tableSize = tableListTemp.size();
      List<Column> columnsTemp = this.getColumns(statement, schemaSub);
      columns.addAll(columnsTemp);
      List<View> viewListTemp = this.getViews(statement, schemaSub);
      long start5 = System.currentTimeMillis();
      Map<String, Schema> schemasMap = new HashMap();
      Map<String, Table> tablesMap = new HashMap();

      for(Schema schema : schemaSub) {
         schemasMap.put(schema.getName(), schema);
      }

      for(Table table : tableList) {
         String key = this.getSchemaTableId(table.getSchema(), table.getName());
         tablesMap.put(key, table);
         Schema schema = (Schema)schemasMap.get(table.getSchema());
         if (schema != null) {
            schema.getTableList().add(table);
         }
      }

      for(View view : viewListTemp) {
         Schema schema = (Schema)schemasMap.get(view.getSchema());
         if (schema != null) {
            schema.getViewList().add(view);
         }
      }

      for(Column column : columns) {
         String key = this.getSchemaTableId(column.getSchema(), column.getTable());
         Table table = (Table)tablesMap.get(key);
         if (table != null) {
            table.getColumnList().add(column);
         }
      }

      long end1 = System.currentTimeMillis();
      System.out.println("Hive schema:" + tableSize + " tables 用时:" + (end1 - start1) / 1000L);
   }

   public Connection getConn(DataBase dataBase) {
      String[] strings = dataBase.getClickhouseJdbcUrl().split("/");
      String db = strings[strings.length - 1];
      db = getClickDatabase(db);
      String address = dataBase.getClickhouseJdbcUrl().split("/" + db)[0];
      int socketTimeout = 600000;
      ClickHouseProperties properties = new ClickHouseProperties();
      properties.setDatabase(db);
      properties.setSocketTimeout(socketTimeout);
      if (StringUtils.isNotEmpty(dataBase.getClickhousePassword())) {
         properties.setPassword(dataBase.getClickhousePassword());
      }

      ClickHouseDataSource clickHouseDataSource = new ClickHouseDataSource(address, properties);
      ClickHouseConnection conn = null;

      try {
         conn = clickHouseDataSource.getConnection();
         return conn;
      } catch (SQLException e) {
         e.printStackTrace();
         return null;
      }
   }

   private static String getClickDatabase(String database) {
      if (database != null && database.contains("?")) {
         database = database.split("[?]")[0];
      }

      return database;
   }

   private List<View> getViews(Statement statement, List<Schema> schemas) throws SQLException {
      String tableName = this.preFix + "views_v";
      ArrayList<String> schemasList = new ArrayList();

      for(Schema x : schemas) {
         if (x != null && x.getName().length() > 0) {
            schemasList.add(x.getName());
         }
      }

      String schemasWhere = StringJoin.join(schemasList, "','");
      List<View> viewList = new ArrayList();
      int pageNum = 1;
      int pageSize = 5000;

      while(true) {
         int offset = (pageNum - 1) * pageSize;
         String sql = "SELECT view_name, database_name, origin_text, expanded_text FROM " + tableName + " WHERE database_name in ('" + schemasWhere + "') limit " + offset + " ,  " + pageSize;
         ResultSet resultSet = statement.executeQuery(sql);

         while(resultSet.next()) {
            String schema = resultSet.getString("database_name");
            viewList.add(new View(schema, resultSet.getString("view_name"), resultSet.getString("expanded_text")));
         }

         if (resultSet.getRow() < pageSize) {
            return viewList;
         }

         resultSet.close();
         ++pageNum;
      }
   }

   private List<Table> getTables(Statement statement, List<Schema> schemas) throws SQLException {
      String tableName = this.preFix + "tables_v";
      ArrayList<String> schemasList = new ArrayList();

      for(Schema x : schemas) {
         if (x != null && x.getName().length() > 0) {
            schemasList.add(x.getName());
         }
      }

      String schemasWhere = StringJoin.join(schemasList, "','");
      List<Table> tables = new ArrayList();
      int pageNum = 1;
      int pageSize = 5000;

      while(true) {
         int offset = (pageNum - 1) * pageSize;
         String sql = "SELECT database_name, table_name FROM " + tableName + " WHERE database_name in ('" + schemasWhere + "') limit " + offset + " ,  " + pageSize;
         ResultSet resultSet = statement.executeQuery(sql);

         while(resultSet.next()) {
            String schema = resultSet.getString("database_name");
            Table table = new Table(resultSet.getString("table_name"), schema);
            table.setRealName(resultSet.getString("table_name"));
            tables.add(table);
         }

         if (resultSet.getRow() < pageSize) {
            return tables;
         }

         resultSet.close();
         ++pageNum;
      }
   }

   private List<Column> getColumns(Statement statement, List<Schema> schemas) throws SQLException {
      int pageNum = 1;
      int pageSize = 5000;
      String tableName = this.preFix + "columns_v";
      ArrayList<String> schemasList = new ArrayList();

      for(Schema x : schemas) {
         if (x != null && x.getName().length() > 0) {
            schemasList.add(x.getName());
         }
      }

      String schemasWhere = StringJoin.join(schemasList, "','");
      List<Column> columns = new ArrayList(5000);

      while(true) {
         int offset = (pageNum - 1) * pageSize;
         String sql = "SELECT column_id, column_name, column_type, table_name, database_name, commentstring, default_value, nullable, unique_constraint, column_length, column_scale  FROM " + tableName + " where database_name in( '" + schemasWhere + "') ORDER BY column_id limit " + offset + " ,  " + pageSize;
         ResultSet resultSet = statement.executeQuery(sql);
         resultSet.setFetchSize(5000);
         Column column = null;

         while(resultSet.next()) {
            column = new Column();
            String schema = resultSet.getString("database_name");
            String table = resultSet.getString("table_name");
            column.setColumnName(resultSet.getString("column_name"));
            column.setSchema(schema);
            column.setTable(table);
            column.setColumnType(resultSet.getString("column_type"));
            column.setLength(resultSet.getObject("column_length") + "");
            Object scale = resultSet.getObject("column_scale");
            column.setDigits(scale == null ? "" : scale.toString());
            String remark = resultSet.getString("commentstring");
            column.setRemark(remark == null ? "" : remark);
            columns.add(column);
         }

         if (resultSet.getRow() < pageSize) {
            return columns;
         }

         resultSet.close();
         ++pageNum;
      }
   }

   public void toUppercase(List<Schema> schemaList) {
      for(Schema schema : schemaList) {
         for(Table table : schema.getTableList()) {
            for(Column column : table.getColumnList()) {
               if (!this.isEmpty(column.getColumnName())) {
                  column.setColumnName(column.getColumnName().toUpperCase());
               }
            }

            if (!this.isEmpty(table.getName())) {
               table.setName(table.getName().toUpperCase());
            }
         }

         for(View view : schema.getViewList()) {
            if (!this.isEmpty(view.getName())) {
               view.setName(view.getName().toUpperCase());
            }
         }

         if (!this.isEmpty(schema.getName())) {
            schema.setName(schema.getName().toUpperCase());
         }
      }

   }

   public List<Schema> getSchema(Statement statement) throws SQLException {
      List<Schema> schemas = new ArrayList();
      String table = this.preFix + "databases_v";
      String sql = "select database_name  from " + table;
      ResultSet resultSet = statement.executeQuery(sql);

      while(resultSet.next()) {
         Schema schema = new Schema(resultSet.getString("database_name"));
         schema.setRealName(resultSet.getString("database_name"));
         schemas.add(schema);
      }

      resultSet.close();
      return schemas;
   }

   public List<Table> getTable(Statement statement, String schema) throws SQLException {
      List<Table> tables = new ArrayList();
      return tables;
   }

   public List<Column> getColumn(Statement statement, String schema, String table) throws SQLException {
      List<Column> columns = new ArrayList();
      String sql = "desc " + schema + "." + table;
      ResultSet resultSet = statement.executeQuery(sql);

      while(resultSet.next()) {
         columns.add(new Column(resultSet.getString("COL_NAME"), schema, table, resultSet.getString("DATA_TYPE")));
      }

      resultSet.close();
      return columns;
   }

   public List<View> getView(Statement statement, String schema) throws SQLException {
      List<View> views = new ArrayList();
      return views;
   }

   public List<String> getList(Statement statement, String schema) throws SQLException {
      List<String> tables = new ArrayList();
      String sql = "show tables";
      ResultSet resultSet = statement.executeQuery(sql);

      while(resultSet.next()) {
         tables.add(resultSet.getString("TAB_NAME"));
      }

      resultSet.close();
      return tables;
   }

   public String getTableType(Statement statement, String tableName, String schemaName) throws Exception {
      String result = "";
      String sql = "desc formatted " + tableName;

      try {
         ResultSet resultSet = statement.executeQuery(sql);

         while(resultSet.next()) {
            if (this.isExistColumn(resultSet, "CATEGORY")) {
               String col_name = resultSet.getString("CATEGORY").trim();
               if (col_name.equalsIgnoreCase("Table Type:")) {
                  result = resultSet.getString("ATTRIBUTE").trim();
                  break;
               }
            } else {
               String col_name = resultSet.getString("COL_NAME").trim();
               if (col_name.equalsIgnoreCase("Table Type:")) {
                  result = resultSet.getString("DATA_TYPE").trim();
                  break;
               }
            }
         }

         resultSet.close();
         return result;
      } catch (Exception e) {
         System.out.println(sql);
         System.out.println(schemaName);
         e.printStackTrace();
         throw e;
      }
   }

   public boolean isExistColumn(ResultSet rs, String columnName) {
      try {
         return rs.findColumn(columnName) > 0;
      } catch (SQLException var4) {
         return false;
      }
   }

   public String getCreateView(Statement statement, String viewName) throws SQLException {
      String create_view = "";
      String sql = "show create table " + viewName;

      ResultSet resultSet;
      for(resultSet = statement.executeQuery(sql); resultSet.next(); create_view = create_view + resultSet.getString("createtab_stmt").replaceAll("`", "") + "\n") {
      }

      resultSet.close();
      return create_view;
   }

   private boolean isEmpty(String str) {
      return str == null || str.length() == 0;
   }
}
