/**
 * 项目名称:
 * 创建日期: 2023/4/26
 */
package org.ts.ddcs.hbase;

import cn.hutool.core.util.IdUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.*;

/**
 * 创建日期: 2023/4/26
 * Title: 文件所属模块（必须填写）
 * Description：对本文件的详细描述，原则上不能少于30字
 * @author DSH
 * @version 1.0
 * Remark：认为有必要的其他信息
 * @mender：（文件的修改者，文件创建者之外的人）
 */
@Slf4j
public class HBaseService {

	/** 默认列 - 数据上报时间 */
	private final String COLUMN_REPORT_TIME = "report_time";

	/** 默认列 - 数据创建时间 */
	private final String COLUMN_CREATE_TIME = "create_time";

	/** 默认列 - 数据创建时间 */
	private final String COLUMN_CREATE_TIME_STR = "create_time_str";

	/**
	 * 声明静态配置
	 */
	private Configuration conf = null;
	private Connection connection = null;

	public HBaseService(Configuration conf, boolean enable) {
		if (enable) {
			this.conf = conf;
			try {
				log.info("HBase连接初始化...");
				connection = ConnectionFactory.createConnection(this.conf);
			} catch (IOException e) {
				log.error("获取HBase连接失败");
			}
		} else {
			log.info("HBase连接未初始化！");
		}
	}

	/**
	 * 功能: 判断表是否存在
	 * 创建日期: 2023/6/7
	 * @param tableName 表名
	 * @return: boolean
	 */
	public boolean isTableExists(String tableName) {
		boolean exists = false;
		try {
			TableName table = TableName.valueOf(tableName);
			exists = connection.getAdmin().tableExists(table);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return exists;
	}

	/**
	 * 功能: 创建表
	 * 创建日期: 2023/6/7
	 * @param tableName 表名
	 * @param columnFamily 列族
	 * @return: boolean
	 */
	public boolean createTable(String tableName, List<String> columnFamily) {
		return createTable(tableName, columnFamily, null);
	}

	/**
	 * 功能: 预分区创建表
	 * 创建日期: 2023/6/7
	 * @param tableName 表名
	 * @param columnFamily 列族
	 * @param keys 分区集合
	 * @return: boolean
	 */
	public boolean createTable(String tableName, List<String> columnFamily, List<String> keys) {
		if (isTableExists(tableName)) {
			log.debug("HBase中表 {} 已存在。", tableName);
			return false;
		}
		Admin admin = null;
		try {
			/*TableName table = TableName.valueOf(tableName);
			HTableDescriptor desc = new HTableDescriptor(table);
			for (String cf : columnFamily) {
				desc.addFamily(new HColumnDescriptor(cf));
			}*/
			admin = connection.getAdmin();
			List<ColumnFamilyDescriptor> familyDescriptors = new ArrayList<>(columnFamily.size());
			for (String column : columnFamily) {
				familyDescriptors.add(ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(column)).build());
			}
			TableDescriptor desc = TableDescriptorBuilder.newBuilder(TableName.valueOf(tableName)).setColumnFamilies(familyDescriptors).build();
			if (keys == null) {
				admin.createTable(desc);
			} else {
				byte[][] splitKeys = getSplitKeys(keys);
				admin.createTable(desc, splitKeys);
			}
			return true;
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			close(admin, null, null);
		}
		return false;
	}

	/**
	 * 功能: 删除表
	 * 创建日期: 2023/6/7
	 * @param tableName 表名
	 * @return: boolean
	 */
	public boolean dropTable(String tableName) {
		Admin admin = null;
		try {
			admin = connection.getAdmin();
			if (admin.tableExists(TableName.valueOf(tableName))) {
				admin.disableTable(TableName.valueOf(tableName));
				admin.deleteTable(TableName.valueOf(tableName));
				log.debug("表【" + tableName + "】删除成功！");
			}
		} catch (IOException e) {
			log.error(MessageFormat.format("表【{0}】删除失败。", tableName), e);
			return false;
		} finally {
			close(admin, null, null);
		}
		return true;
	}

	/**
	 * 功能: 插入数据（单条）
	 * 创建日期: 2023/6/7
	 * @param tableName 表名
	 * @param rowKey rowKey
	 * @param columnFamily 列族
	 * @param column 列
	 * @param value 值
	 * @param reportTime 数据填报时间，毫秒
	 * @return: boolean
	 */
	public boolean putData(String tableName, String rowKey, String columnFamily, String column, String value, Long reportTime) {
		return putData(tableName, rowKey, columnFamily, Arrays.asList(column), Arrays.asList(value), reportTime);
	}

	/**
	 * 功能: 插入数据（批量）
	 * 创建日期: 2023/6/7
	 * @param tableName 表名
	 * @param rowKey rowKey
	 * @param columnFamily 列族
	 * @param columns 列
	 * @param values 值
	 * @return: boolean
	 */
	public boolean putData(String tableName, String rowKey, String columnFamily, List<String> columns, List<String> values, Long reportTime) {
		Table table = null;
		try {
			table = connection.getTable(TableName.valueOf(tableName));
			Put put = new Put(Bytes.toBytes(rowKey));
			for (int i = 0; i < columns.size(); i++) {
				if (columns.get(i) != null && values.get(i) != null) {
					put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(columns.get(i)), Bytes.toBytes(values.get(i)));
				} else {
					throw new NullPointerException(MessageFormat.format("列名和列数据都不能为空,column:{0},value:{1}" , columns.get(i), values.get(i)));
				}
			}
			if (reportTime != null) {
				put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(COLUMN_REPORT_TIME), Bytes.toBytes(reportTime.longValue())); // 填报时间，可能不同于创建时间，可能用于排序、过滤查询
			}
			put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(COLUMN_CREATE_TIME), Bytes.toBytes(System.currentTimeMillis())); // 创建时间
//			put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(COLUMN_CREATE_TIME_STR), Bytes.toBytes(Long.toString(System.currentTimeMillis()))); // 创建时间字符串
			table.put(put);
			table.close();
			return true;
		} catch (IOException e) {
			log.error(MessageFormat.format("插入数据失败,tableName:{0},rowKey:{1},columnFamily:{2}", tableName, rowKey, columnFamily), e);
			e.printStackTrace();
			return false;
		} finally {
			close(null, null, table);
		}
	}


	public boolean putData(String tableName, String rowKey, String columnFamily, List<String> columns, List<String> values) {
		Table table = null;
		try {
			table = connection.getTable(TableName.valueOf(tableName));
			Put put = new Put(Bytes.toBytes(rowKey));
			for (int i = 0; i < columns.size(); i++) {
				if (columns.get(i) != null && values.get(i) != null) {
					put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(columns.get(i)), Bytes.toBytes(values.get(i)));
				} else {
					throw new NullPointerException(MessageFormat.format("列名和列数据都不能为空,column:{0},value:{1}" , columns.get(i), values.get(i)));
				}
			}
//			if (reportTime != null) {
//				put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(COLUMN_REPORT_TIME), Bytes.toBytes(reportTime.longValue())); // 填报时间，可能不同于创建时间，可能用于排序、过滤查询
//			}
//			put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(COLUMN_CREATE_TIME), Bytes.toBytes(System.currentTimeMillis())); // 创建时间
//			put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(COLUMN_CREATE_TIME_STR), Bytes.toBytes(Long.toString(System.currentTimeMillis()))); // 创建时间字符串
			table.put(put);
			table.close();
			return true;
		} catch (IOException e) {
			log.error(MessageFormat.format("插入数据失败,tableName:{0},rowKey:{1},columnFamily:{2}", tableName, rowKey, columnFamily), e);
			e.printStackTrace();
			return false;
		} finally {
			close(null, null, table);
		}
	}

	/**
	 * 功能: 获取数据（全表数据）
	 * 创建日期: 2023/6/7
	 * @param tableName 表名
	 * @return: java.util.List<java.util.Map<java.lang.String,java.lang.String>>
	 */
	public List<Map<String, Object>> getData(String tableName) {
		List<Map<String, Object>> list = new ArrayList<>();
		Table table = null;
		ResultScanner rs = null;
		try {
			table = connection.getTable(TableName.valueOf(tableName));
			Scan scan = new Scan();
			rs = table.getScanner(scan);
			for (Result result : rs) {
				HashMap<String, Object> map = new HashMap<>();
				// rowkey
				String row = Bytes.toString(result.getRow());
				map.put("row", row);
				for (Cell cell : result.listCells()) {
					// 列族
					String family = Bytes.toString(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength());
					// 列
					String qualifier = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength());
					// 值
					Object data = null;
					if (COLUMN_CREATE_TIME.equals(qualifier) || COLUMN_REPORT_TIME.equals(qualifier)) {
						data = Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
					} else {
						data = Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
					}
					// Timestamp
					long timestamp = cell.getTimestamp();
					map.put(family + ":" + qualifier, data);
				}
				list.add(map);
			}
			table.close();
		} catch (IOException e) {
			log.error(MessageFormat.format("获取数据（全表数据）失败,tableName:{0}", tableName), e);
			e.printStackTrace();
		} finally {
			close(null, rs, table);
		}
		return list;
	}

	public List<Map<String, Object>> getData(String tableName, Scan scan) {
		List<Map<String, Object>> list = new ArrayList<>();
		Table table = null;
		ResultScanner rs = null;
		try {

			table = connection.getTable(TableName.valueOf(tableName));
			if (scan == null) {
				scan = new Scan();
			}
			rs = table.getScanner(scan);
			for (Result result : rs) {
				HashMap<String, Object> map = new HashMap<>();
				// rowkey
				String row = Bytes.toString(result.getRow());
				map.put("row", row);
				for (Cell cell : result.listCells()) {
					// 列族
					String family = Bytes.toString(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength());
					// 列
					String qualifier = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength());
					// 值
					Object data = null;
					if (COLUMN_CREATE_TIME.equals(qualifier) || COLUMN_REPORT_TIME.equals(qualifier)) {
						data = Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
					} else {
						data = Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
					}
					// Timestamp
					long timestamp = cell.getTimestamp();
					map.put(family + ":" + qualifier, data);
				}
				list.add(map);
			}
			table.close();
		} catch (IOException e) {
			log.error(MessageFormat.format("获取数据（全表数据）失败,tableName:{0}", tableName), e);
			e.printStackTrace();
		} finally {
			close(null, rs, table);
		}
		return list;
	}

	/**
	 * 功能: 获取数据（根据rowkey）
	 * 创建日期: 2023/6/7
	 * @param tableName 表名
	 * @param rowKey rowKey
	 * @return: java.util.Map<java.lang.String,java.lang.String>
	 */
	public Map<String, Object> getData(String tableName, String rowKey) {
		HashMap<String, Object> map = new HashMap<>();
		try {
			Table table = connection.getTable(TableName.valueOf(tableName));
			Get get = new Get(Bytes.toBytes(rowKey));
			Result result = table.get(get);
			if (result != null && !result.isEmpty()) {
				map.put("row", rowKey);
				for (Cell cell : result.listCells()) {
					// 列族
					String family = Bytes.toString(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength());
					// 列
					String qualifier = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength());
					// 值
					Object data = null;
					if (COLUMN_CREATE_TIME.equals(qualifier) || COLUMN_REPORT_TIME.equals(qualifier)) {
						data = Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
					} else {
						data = Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
					}
					map.put(family + ":" + qualifier, data);
				}
			}
			table.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return map;
	}

	/**
	 * 功能: 获取数据（根据rowkey，列族，列）
	 * 创建日期: 2023/6/7
	 * @param tableName 表名
	 * @param rowKey
	 * @param columnFamily 列族
	 * @param columnQualifier 列
	 * @return: java.lang.String
	 */
	public String getData(String tableName, String rowKey, String columnFamily, String columnQualifier) {
		String data = "";
		try {
			Table table = connection.getTable(TableName.valueOf(tableName));
			Get get = new Get(Bytes.toBytes(rowKey));
			get.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(columnQualifier));
			Result result = table.get(get);
			if (result != null && !result.isEmpty()) {
				Cell cell = result.listCells().get(0);

				// 列族
				String family = Bytes.toString(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength());
				// 列
				String qualifier = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength());
				if (COLUMN_CREATE_TIME.equals(qualifier) || COLUMN_REPORT_TIME.equals(qualifier)) {
					data = Long.toString(Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
				} else {
					data = Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
				}
			}
			table.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return data;
	}

	/**
	 * 功能: 删除数据（根据rowkey）
	 * 创建日期: 2023/6/7
	 * @param tableName 表名
	 * @param rowKey
	 * @return: void
	 */
	public void deleteData(String tableName, String rowKey) throws IOException {
		Table table = connection.getTable(TableName.valueOf(tableName));
		Delete delete = new Delete(Bytes.toBytes(rowKey));
		table.delete(delete);
		table.close();
	}

	/**
	 * 功能: 删除数据（根据rowkey，列族）
	 * 创建日期: 2023/6/7
	 * @param tableName 表名
	 * @param rowKey
	 * @param columnFamily 列族
	 * @return: void
	 */
	public void deleteData(String tableName, String rowKey, String columnFamily) throws IOException {
		Table table = connection.getTable(TableName.valueOf(tableName));
		Delete delete = new Delete(Bytes.toBytes(rowKey));
		delete.addFamily(columnFamily.getBytes());
		table.delete(delete);
		table.close();
	}

	/**
	 * 功能: 删除数据（根据rowkey，列族）
	 * 创建日期: 2023/6/7
	 * @param tableName 表名
	 * @param rowKey
	 * @param columnFamily 列族
	 * @param column 列
	 * @return: void
	 */
	public void deleteData(String tableName, String rowKey, String columnFamily, String column) throws IOException {
		Table table = connection.getTable(TableName.valueOf(tableName));
		Delete delete = new Delete(Bytes.toBytes(rowKey));
		delete.addColumn(columnFamily.getBytes(), column.getBytes());
		table.delete(delete);
		table.close();
	}

	/**
	 * 功能: 删除数据（多行）
	 * 创建日期: 2023/6/7
	 * @param tableName 表名
	 * @param rowKeys rowKey集合
	 * @return: void
	 */
	public void deleteData(String tableName, List<String> rowKeys) throws IOException {
		Table table = connection.getTable(TableName.valueOf(tableName));
		List<Delete> deleteList = new ArrayList<>();
		for (String row : rowKeys) {
			Delete delete = new Delete(Bytes.toBytes(row));
			deleteList.add(delete);
		}
		table.delete(deleteList);
		table.close();
	}

	/**
	 * 功能: 分区【10, 20, 30】 -> ( ,10] (10,20] (20,30] (30, )
	 * 创建日期: 2023/6/7
	 * @param keys 分区集合[10, 20, 30]
	 * @return: byte[][]
	 */
	private byte[][] getSplitKeys(List<String> keys) {
		byte[][] splitKeys = new byte[keys.size()][];
		TreeSet<byte[]> rows = new TreeSet<>(Bytes.BYTES_COMPARATOR);
		for (String key : keys) {
			rows.add(Bytes.toBytes(key));
		}
		int i = 0;
		for (byte[] row : rows) {
			splitKeys[i] = row;
			i++;
		}
		return splitKeys;
	}

	/**
	 * 功能: 查询库中所有表的表名
	 * 创建日期: 2023/6/8
	 * @param
	 * @return: java.util.List<java.lang.String>
	 */
	public List<String> getAllTableNames() {
		List<String> result = new ArrayList<>();
		Admin admin = null;
		try {
			admin = connection.getAdmin();
			TableName[] tableNames = admin.listTableNames();
			for (TableName tableName : tableNames) {
				result.add(tableName.getNameAsString());
			}
		} catch (IOException e) {
			log.error("获取所有表的表名失败", e);
		} finally {
			close(admin, null, null);
		}
		return result;
	}

	/**
	 * 功能: 关闭流
	 * 创建日期: 2023/6/8
	 * @param admin
	 * @param rs
	 * @param table
	 * @return: void
	 */
	private void close(Admin admin, ResultScanner rs, Table table) {
		if (admin != null) {
			try {
				admin.close();
			} catch (IOException e) {
				log.error("关闭Admin失败", e);
			}
		}

		if (rs != null) {
			rs.close();
		}

		if (table != null) {
			try {
				table.close();
			} catch (IOException e) {
				log.error("关闭Table失败", e);
			}
		}
	}

	/**
	 * 功能: 创建一个新的rowKey（方便排序和查找最新记录）
	 * 创建日期: 2023/9/20
	 * @param
	 * @return: java.lang.String
	 */
	public String createRowKey() {
		return String.valueOf(Long.MAX_VALUE - System.currentTimeMillis()) + "-" + IdUtil.fastSimpleUUID();
	}

}
