package com.crunii.micro.ds.single.druid.filter;

import com.alibaba.druid.DbType;
import com.alibaba.druid.filter.FilterEventAdapter;
import com.alibaba.druid.proxy.jdbc.DataSourceProxy;
import com.alibaba.druid.proxy.jdbc.JdbcParameter;
import com.alibaba.druid.proxy.jdbc.StatementProxy;
import com.alibaba.druid.util.JdbcUtils;
import com.crunii.micro.common.constants.DSConstants;
import com.crunii.micro.common.spring.SpringUtil;
import com.crunii.micro.domain.dto.CallHolder;
import com.crunii.micro.domain.dto.Worker;
import com.crunii.micro.domain.entitysql.main.TSqlLogS;
import com.crunii.micro.platform.log.SqlLogHandler;
import com.mysql.jdbc.PreparedStatement.BatchParams;
import com.mysql.jdbc.StatementImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.util.StringUtils;

import java.io.UnsupportedEncodingException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 田平 create 2019年5月14日下午3:15:41
 */
@Slf4j
public class ExecSqlFilter extends FilterEventAdapter {

	protected DataSourceProxy dataSource;

	@Autowired
	@Lazy
	private SqlLogHandler sqlLogHandler;

	public ExecSqlFilter() {
	}

	@Override
	public void init(DataSourceProxy dataSource) {
		this.dataSource = dataSource;
	}

	@Override
	public void statementExecuteBefore(StatementProxy statement, String sql) {
		doSqlLog(statement, sql, false);
	}

	@Override
	public void statementExecuteBatchBefore(StatementProxy statement) {
		doSqlLog(statement, statement.getBatchSql(), true);
	}

	@Override
	public void statementExecuteQueryBefore(StatementProxy statement, String sql) {
		doSqlLog(statement, sql, false);
	}

	@Override
	public void statementExecuteUpdateBefore(StatementProxy statement, String sql) {
		doSqlLog(statement, sql, false);
	}

	protected void doSqlLog(StatementProxy statement, String sql, boolean batch) {

		if (StringUtils.hasText(sql) && DSConstants.containLogTable(sql)) {
			TSqlLogS sqlLog = new TSqlLogS();
			sqlLog.setExecSql(sql);
			StringBuilder bufParam = new StringBuilder();
			bufParam.append("Parameters : [");
			if (batch) {
				List<Object> params = new ArrayList<>();
				DbType dbType = DbType.of(dataSource.getDbType());
				if (dbType == DbType.mysql) {
					StatementImpl impl = (StatementImpl) statement.getRawObject();
					params = impl.getBatchedArgs();
				} else {
					log.warn("batch sql log param record not support dbType:{}", dataSource.getDbType());
				}
				params.forEach(obj -> {
					BatchParams batchParams = (BatchParams) obj;
					bufParam.append(Arrays.stream(batchParams.parameterStrings).map(bytes -> {
						String value = null;
						try {
							value = new String(bytes, "utf-8");
						} catch (UnsupportedEncodingException e) {
							log.warn("sql log batch sql param to string error!", e);
						}
						return value;
					}).collect(Collectors.joining(", ", "", "; ")));
				});
			} else {
				for (int i = 0, parametersSize = statement.getParametersSize(); i < parametersSize; ++i) {
					JdbcParameter parameter = statement.getParameter(i);
					if (i != 0) {
						bufParam.append(", ");
					}
					if (parameter == null) {
						continue;
					}
					int sqlType = parameter.getSqlType();
					Object value = parameter.getValue();
					switch (sqlType) {
					case Types.NULL:
						bufParam.append("NULL");
						break;
					default:
						bufParam.append(value);
						break;
					}
				}
			}
			bufParam.append("]");
			sqlLog.setExecParam(bufParam.toString());

			StringBuilder bufParamType = new StringBuilder();
			bufParamType.append("Types : [");
			for (int i = 0, parametersSize = statement.getParametersSize(); i < parametersSize; ++i) {
				JdbcParameter parameter = statement.getParameter(i);
				if (i != 0) {
					bufParamType.append(", ");
				}
				if (parameter == null) {
					continue;
				}
				int sqlType = parameter.getSqlType();
				bufParamType.append(JdbcUtils.getTypeName(sqlType));
			}
			bufParamType.append("]");
			sqlLog.setExecParamType(bufParamType.toString());

			CallHolder holder = CallHolder.call.get();
			if (holder != null) {
				Worker worker = holder.getWorker();
				if (worker != null) {
					sqlLog.setLoginId(worker.getLoginId());
					sqlLog.setLoginCode(worker.getLoginCode());
					sqlLog.setWorkerName(worker.getWorkerName());
				}
				sqlLog.setCallSerial(holder.getCallSerial());
				sqlLog.setCallUrl(holder.getCallUrl());
			}
			sqlLog.setCreateTime(new Date());

			//异步发送日志
			sqlLogHandler.doSqlLog(sqlLog);
		}

	}
}