package com.eactive.eai.rms.onl.common.sql;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 2022. 04. 21. ExtractSqlService 하위 클래스 Usage
 * com.eactive.eai.rms.onl.common.sql.EaiMsgSqlService.java 및 아래 함수 설명 참조
 *  
 * @author sunwook
 */
@Service
public class ExtractSqlService {
	
	protected final Logger logger = Logger.getLogger(getClass());
	
	@Autowired private ExtractSqlDao dao;
	
	/**
	 * 기존 데이터 조회용 함수 
	 * 
	 * @param targetTableName
	 * 	테이블 명 입력
	 * 
	 * @param keyColName
	 * 	keyValues의 값을 넣어 조회할 컬럼명 입력
	 * 
	 * @param keyValues
	 * 	keyColName에 들어갈 값의 목록. String type이라면 해당 값 하나만 검색, String[] type이라면 SQL IN query로 해당 값들을 포함한 모든 row 검색.
	 * 
	 * @return
	 * 	이 함수의 return은 (1)function extractKeys의 parameter queryResult (2)function makeSql의 parameter queryResult에 사용된다.
	 * 
	 * @throws Exception
	 */
	protected List<Map<String, Object>> select(String targetTableName, String keyColName, Object keyValues) throws Exception {
		
		Map<String, Object> paramMap = new HashMap<>();
		
		if(keyValues instanceof String) {
			
			if(StringUtils.isBlank((String) keyValues)) {
				return new ArrayList<>();
			}
			
			paramMap.put("tableName", targetTableName);
			paramMap.put("keyColName", keyColName);
			paramMap.put("keyValue", keyValues); /** one keyValue **/
			
			return dao.selectByKey(paramMap);
			
		} else if (keyValues instanceof String[]) {
			
			if(((String[])keyValues).length == 0) {
				return new ArrayList<>();
			}

			paramMap.put("tableName", targetTableName);
			paramMap.put("keyColName", keyColName);
			paramMap.put("keyValues", keyValues); /** many keyValues **/
			
			return dao.selectByKeys(paramMap);
			
		} else {
			throw new Exception("EaiMsgSqlService.select] String 혹은 String[] 의 keyValues만 지원합니다.");
		}
		
	}
	
	/**
	 * 쿼리 결과로부터 타 테이블 조회용 key값의 목록을 추출하기 위한 함수.
	 * 
	 * @param queryResult
	 * 	function select의 return이 입력되어야 함. 타 service의 selectList의 return도 사용 가능.
	 * 
	 * @param colNames
	 * 	String type이라면 해당 이름의 컬럼 하나에서 value 추출, String[] type이라면 해당 값으로 지정된 모든 컬럼에서 value 추출.
	 * 
	 * @return
	 * 	추출한 value 목록을 String[] 형태로 반환.
	 * 
	 * @throws Exception
	 */
	protected String[] extractKeys(List<Map<String, Object>> queryResult, Object colNames) throws Exception {
		
		if(colNames instanceof String) {
			
			String colName = (String) colNames;
			String[] result = new String[queryResult.size()];
			int index = 0;
			
			for(Map<String, Object> row : queryResult) {
				result[index++] = (String) row.get(colName);
			}
			
			return result;
			
		} else if (colNames instanceof String[]) {
			
			String[] colNamesArr = (String[]) colNames;
			String[] result = new String[queryResult.size()*colNamesArr.length];
			int index = 0;
			
			for(Map<String, Object> row : queryResult) {
				for(String colName : colNamesArr) {
					result[index++] = (String) row.get(colName);
				}
			}
			
			return result;
			
		} else  {
			throw new Exception("EaiMsgSqlService.extractKeys] String 혹은 String[] 의 colNames만 지원합니다.");
		}
	}
	
	/**
	 * 쿼리 결과로부터 타 테이블 조회용 key값의 목록을 추출하기 위한 함수.
	 * 
	 * @param queryResult
	 * 	상기와 동일
	 * 
	 * @param colNames
	 * 	상기와 동일
	 * 
	 * @param regExp
	 * 	추출시, 입력한 정규식에 match하는 결과로 변환하여 배열에 저장함.
	 *  
	 * @return
	 * 	상기와 동일
	 * 
	 * @throws Exception
	 */
	protected String[] extractKeys(List<Map<String, Object>> queryResult, Object colNames, String regExp) throws Exception {
		
		if(colNames instanceof String) {
			
			String colName = (String) colNames;
			String[] result = new String[queryResult.size()];
			int index = 0;
			
			for(Map<String, Object> row : queryResult) {
				String rowStr = (String) row.get(colName);
				Pattern pattern = Pattern.compile(regExp);
				Matcher matcher = pattern.matcher(rowStr);
				if(matcher.find(0)) {
					result[index++] = matcher.group(0);
				} else {
					result[index++] = "";
				}
			}
			
			return result;
			
		} else if (colNames instanceof String[]) {
			
			String[] colNamesArr = (String[]) colNames;
			String[] result = new String[queryResult.size()*colNamesArr.length];
			int index = 0;
			
			for(Map<String, Object> row : queryResult) {
				for(String colName : colNamesArr) {
					String rowStr = (String) row.get(colName);
					Pattern pattern = Pattern.compile(regExp);
					Matcher matcher = pattern.matcher(rowStr);
					if(matcher.find(0)) {
						result[index++] = matcher.group(0);
					} else {
						result[index++] = "";
					}
				}
			}
			
			return result;
			
		} else  {
			throw new Exception("EaiMsgSqlService.extractKeys] String 혹은 String[] 의 colNames만 지원합니다.");
		}
	}
	
	/*
	 * PreparedString 의 특정 부분을 쿼리 결과의 특정 컬럼 값으로 교체
	 * 
	 * ex) 	preparedString : "TerminalInfo{?_?}"
	 * 		seat : ?
	 * 		colValues : {"SETBK", "KY0090"}
	 * 		replaced : TerminalInfo{SETBK_KY0090}
	 */
	public String[] extractKeys(List<Map<String, Object>> queryResult, PreparedString preStr, String...colNames) throws Exception {
		
		String[] result = new String[queryResult.size()];
		
		int index = 0;
		for(Map<String, Object> row : queryResult) {
			String[] colValues = new String[colNames.length];
			int colValueIndex = 0;
			for(String colName : colNames) {
				colValues[colValueIndex++] = (String) row.get(colName); 
			}
			
			result[index++] = preStr.replaceSeatsToArgs(colValues);
		}
		
		return result;
	}
	
	/**
	 * 사용자에게 반환할 SQL문을 제작하는 함수.
	 * 
	 * @param tableName
	 * 	테이블명 입력
	 * 
	 * @param pkColName
	 * 	DELETE T WHERE pkColName = keyVals 제작시 사용.
	 * 
	 * @param keyVals
	 * 	DELETE T WHERE pkColName = keyVals 제작시 사용. keyVals 가 String[] type이라면 DELETE T WHERE pkColName IN (keyVals[]) 형태로 반환함.
	 * 
	 * @param queryResult
	 * 	INSERT 쿼리제작용
	 * 
	 * @return
	 * 	DELETE와 INSERT SQL문 세트로 반환.
	 * 
	 * @throws Exception
	 */
	protected String makeSql(String tableName, String pkColName, Object keyVals, List<Map<String, Object>> queryResult) throws Exception {
		if(queryResult.size() > 0) {
			StringBuilder sb = new StringBuilder();
			sb.append(makeDeleteSql(tableName, pkColName, keyVals));
			sb.append(makeInsertSql(tableName, queryResult));
			return sb.toString();
		} else {
			return "";
		}
	}
	
	protected String makeInsertSql(String tableName, List<Map<String, Object>> queryResults) {
		
		StringBuilder result = new StringBuilder();
		
		for(Map<String, Object> row : queryResults) {
			
			StringBuilder sql = new StringBuilder();
			StringBuilder cols = new StringBuilder();
			StringBuilder vals = new StringBuilder();
			
			for(String key : row.keySet()) {
				
				cols.append(key);
				cols.append(",");
				
				if(row.get(key) != null) {
					vals.append("'");
					vals.append(row.get(key));
					vals.append("'");
				} else {
					vals.append(row.get(key));
				}
				
				vals.append(",");
				
			}
			
			sql.append("INSERT INTO ");
			sql.append(tableName);
			sql.append(" (");
			sql.append(StringUtils.removeEnd(cols.toString(), ","));
			sql.append(") VALUES (");
			sql.append(StringUtils.removeEnd(vals.toString(), ","));
			sql.append(");");
			
			result.append(sql.toString());
			result.append("\n");
		}
		
		result.append("\n");
		
		return result.toString();
	}
	
	protected String makeDeleteSql(String tableName, String keyColName, Object keyValues) throws Exception {
		
		StringBuilder result = new StringBuilder();
		
		if(keyValues instanceof String) {
			
			result.append("DELETE ");
			result.append(tableName);
			result.append(" WHERE ");
			result.append(keyColName);
			result.append(" = '");
			result.append(keyValues);
			result.append("';");
			result.append("\n");
			
			return result.toString();
			
		} else if (keyValues instanceof String[]) {

			String[] keyValuesArr = (String[]) keyValues;
			StringBuilder valSb = new StringBuilder();
			
			for(String val : keyValuesArr) {
				if(StringUtils.isBlank(val)) { continue; }
				valSb.append("'");
				valSb.append(val);
				valSb.append("'");
				valSb.append(",");
			}
			
			result.append("DELETE ");
			result.append(tableName);
			result.append(" WHERE ");
			result.append(keyColName);
			result.append(" IN (");
			result.append(StringUtils.removeEnd(valSb.toString(), ","));
			result.append(");");
			result.append("\n");
			
			return result.toString();
			
		} else {
			throw new Exception("EaiMsgSqlService.makeDeleteSql] String 혹은 String[] 의 keyValues만 지원합니다.");
		}
		
	}

}