package com.eactive.eai.adapter.db.sql;

import com.eactive.eai.adapter.AdapterVO;
import com.eactive.eai.adapter.RequestDispatcher;
import com.eactive.eai.adapter.db.DBAdapterKeys;
import com.eactive.eai.adapter.wca.jdbc.JDBCException;
import com.eactive.eai.common.message.EAIMessageKeys;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.common.util.ServiceLocator;

import javax.sql.DataSource;
import java.sql.*;
import java.util.LinkedHashMap;
import java.util.Properties;

public class DBInboudSqlAdapter {
	public static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
	
	private boolean isLocalTest = false;
	
	
	private String logPrefix = "";
	private boolean isRunning = false;
	private boolean stopRunning = false;
	
	private AdapterVO vo;
	private Properties configProperties = null;
	
	public boolean isRunningState() {
		return isRunning;
	}
	
	public void setAdapterVO(AdapterVO vo) {
		this.vo = vo;
	}
	
	public void setProperties(Properties p) {
		this.configProperties = p;
	}
	
	public void stop() {
		stopRunning = true;
	}
	
	public DBInboudSqlAdapter(AdapterVO vo, Properties configProperties) {
		super();
		this.vo = vo;
		this.configProperties = configProperties;
		if(vo == null) {
			logPrefix = this.getClass().getSimpleName() +"] ";
		}
		else {
			logPrefix = vo.getName() +"] ";
		}
		String isLocal = System.getProperty("isLocal");
		if(isLocal != null && "true".equalsIgnoreCase(isLocal)) {
			isLocalTest = true;
		}
		// System.out.println("$$$ DBInboudSqlAdapter] isLocalTest = " + isLocalTest);
	}

	public boolean execute() {
		isRunning = true;
		int chunkSize = 15;
		Connection con = null;
		PreparedStatement pstmt = null;
		PreparedStatement pstmtUpdate = null;
	    ResultSet rs = null;
	    
		String datasoureName = configProperties.getProperty(DBAdapterKeys.DATASOURCE) ;
		String selectQuery = configProperties.getProperty(DBAdapterKeys.SELECT) ;
		String updateQuery = configProperties.getProperty(DBAdapterKeys.UPDATE) ;
		
		int runInterval = 0; 
		String runIntervalStr = configProperties.getProperty(DBAdapterKeys.RUN_INTVL) ;
		try {
			runInterval = Integer.parseInt(runIntervalStr);
		}
		catch(Exception e) {
			runInterval = 0;
		}
		
		String runRetry = configProperties.getProperty(DBAdapterKeys.RUN_RETRY, "N") ; // Y | N
		
		int runRetryCnt = 0; 
		String runRetryCntStr = configProperties.getProperty(DBAdapterKeys.RUN_RETRYCNT, "0") ;
		try {
			runRetryCnt = Integer.parseInt(runRetryCntStr);
		}
		catch(Exception e) {
			runRetryCnt = 0;
		}
		
		int runRetryInterval = 0; 
		String runRetryIntervalStr = configProperties.getProperty(DBAdapterKeys.RUN_RETRYINTVL, "0") ;
		try {
			runRetryInterval = Integer.parseInt(runRetryIntervalStr);
		}
		catch(Exception e) {
			runRetryInterval = 0;
		}
		
		int rowIndex = 0;
        int colCount = 0;
		try {
			if(datasoureName != null && datasoureName.trim().length() > 0) {
				con = getDataSourceConnection(datasoureName);
			}
			else {
				con = getJDBCConnection(configProperties);
			}
			// default : row unit commit
			con.setAutoCommit(true);
			
			// select chunk record 
			pstmt = con.prepareStatement(selectQuery);
            rs = pstmt.executeQuery();
            
            rowIndex = 0;
            colCount = rs.getMetaData().getColumnCount();
        	debug(String.format("$$$ RUNNING Query [%s] - colCount = %s", selectQuery, colCount) );
        	
        	String rowString = null;
        	
			// LOOP - Inbound Call
            LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
            boolean isSuccess = false;
            int retryExecCounter = 0;
            boolean retryFailed = false;
        	while(rs.next() && !retryFailed){
        		debug(String.format("==> run [%s]", (rowIndex+1)) );
        		if(stopRunning) {
					debug(String.format("$$$ STOP RUNNING stopRunning[%s]", stopRunning) );
					break;
				}
            	
            	for(int i = 1; i <= colCount; i++){
            		String key = rs.getMetaData().getColumnName(i);
            		String value = rs.getString(i);
            		if(value == null){
            			value = "";
            		}
            		map.put(key, value);
            	}
            	
            	if("JSON".equals(configProperties.getProperty(DBAdapterKeys.RUN_MSGTYPE))) {
            		rowString = ResultParser.toJsonString(map);
            	}
            	if("XML".equals(configProperties.getProperty(DBAdapterKeys.RUN_MSGTYPE))) {
            		rowString = ResultParser.toXmlString(map);
            	}
            	
            	debug(String.format("==> run [%s] : ", (rowIndex+1)) +  rowString);
			//-- start sudo test code
//				debug(String.format("Sleep 1 secs - run [%s/%s]", (rowIndex+1), chunkSize) );
				
			//-- end sudo test code
				
				byte[] request = rowString.getBytes();
				byte[] response = null;
				String elinkResponseCode = null;
				while(true) {
					try {
						isSuccess = false;
						RequestDispatcher dispatcher = RequestDispatcher.getRequestDispatcher(vo.getAdapterGroupName());
						response = (byte[]) dispatcher.handle(vo.getName(), request, this.configProperties);
						
						elinkResponseCode = configProperties.getProperty(EAIMessageKeys.ELINK_RET_CODE);
						debug("RequestProcessor.ELINK_RET_CODE ["+ elinkResponseCode +"]" );
						
						// Sunncess check with elink F/W response code ESBEAI111111
						if(EAIMessageKeys.EAI_SUCCESS_CODE.equals(elinkResponseCode)
								|| EAIMessageKeys.EAI_DEFAULT_CODE.equals(elinkResponseCode)) {
							isSuccess = true;
						}
					} catch (Exception e) {
						logger.error("RequestDispatcher call error",e);
					}
					
					if(isSuccess) {
						// check response
						if(response != null) debug("Response Message : " + new String(response));
					
						// if success, update CHK column
						String mappedUpdate = AdapterUtils.getUpdateQuery(updateQuery, map);
						debug("mappedUpdate - "+ mappedUpdate);
						pstmtUpdate = con.prepareStatement(mappedUpdate);
			            int cnt = pstmtUpdate.executeUpdate();
			            debug("mappedUpdate result - "+ cnt);
			            // if success, skip retry
			            break;
					}
					else {
						// if failed, retry or break, check retry options
						if("Y".equals(runRetry)) {
							debug(String.format(">> RETRY [%s / %s]", retryExecCounter, runRetryCnt) );
							
							if(retryExecCounter < runRetryCnt) {
								debug(String.format(">> RETRY runRetry=%s", runRetry) );
								++retryExecCounter;
								if(runRetryInterval > 0) {
									debug("Sleep run.retryinterval - " +runRetryInterval);
									Thread.sleep(runRetryInterval);
								}
								continue;
							}
							else {
								debug(String.format(">> RETRY COMPLETED [%s / %s], STOP JOB", retryExecCounter, runRetryCnt) );
								retryFailed = true;
								break;
							}
						}
						else {
							debug(String.format(">> RETRY Disabled, runRetry=%s", runRetry) );
							retryFailed = true;
							break;
						}
					}
				} // end retry LOOP
				rowIndex++;
				if(runInterval > 0) {
					debug("Sleep run.interval - " +runInterval);
					Thread.sleep(runInterval);
				}
			}
			// end row LOOP
		}
		catch(Exception ex) {
			ex.printStackTrace();
		}
		isRunning = false;
		debug(String.format("JOB Completed. isRunning=%s, executed ROWCount=%s", isRunning, rowIndex) );
		return isRunning;
	}
	
	private Connection getJDBCConnection(Properties p) throws SQLException {
		try {			
			Class.forName(p.getProperty(DBAdapterKeys.DRIVER));
			Connection con = DriverManager.getConnection(p.getProperty(DBAdapterKeys.URL), 
					p.getProperty(DBAdapterKeys.USER), p.getProperty(DBAdapterKeys.PVAL));
			return con;
		} catch (Exception e) {
			throw new SQLException("DBAdapter] Cannot create a JDBC Connection. - " + e.getMessage());
		}
	}
	
	private Connection getDataSourceConnection(String dataSourceName) throws JDBCException
	{
		DataSource dataSource = null;
        try {
            ServiceLocator sl = ServiceLocator.getInstance();
            dataSource = sl.getDataSource(dataSourceName);
            return dataSource.getConnection();
        }  
        catch(Exception e) {
            e.printStackTrace();
            throw new JDBCException("["+dataSourceName+"]을 얻을 수 없습니다.");
        }
	}
	
	private java.sql.Date toDate(String sDate) throws Exception {
		java.sql.Date s = null;
		if(sDate.length() == 10 && sDate.indexOf("-") > 0) {
			s = java.sql.Date.valueOf(sDate);
		}
		else if(sDate.length() == 8) {
			java.util.Date d = new java.text.SimpleDateFormat("yyyyMMdd").parse(sDate);
			s = new java.sql.Date(d.getTime());
		}
		else {
			throw new Exception("Invalid Date String - "+sDate);
		}
		return s;
	}
	
	private void debug(String msg) {
		if(isLocalTest) {
			System.out.println (logPrefix + msg);
		}
		else {
			logger.info(logPrefix + msg);
		}
	}
}
