
package com.openbanking.eai.common.token.client.impl;

import com.eactive.eai.adapter.http.secure.CustomHttpsSocketFactory;
import com.eactive.eai.adapter.http.secure.EasySSLProtocolSocketFactory;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.common.util.ScpDbAgentUtil;
import com.openbanking.eai.common.token.AccessTokenVO;
import com.openbanking.eai.common.token.OAuth2AccessTokenVO;
import com.openbanking.eai.common.token.client.HttpClientAccessTokenService;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpVersion;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.lang3.StringUtils;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;

import java.util.Base64;
import java.util.Date;
import java.util.Properties;


/** 
* 1. 기능 : CODEF API 시스템을 통해 토큰 발급 수행
* 2. 처리 개요 : 
* *     - 2020.09.04 - 속성 정보를 설정 하고 토큰 발급 URL 호출 한다.
* 3. 주의사항 
*
* @author  : 
* @version : v 1.0.0
* @see : HttpClientAccessTokenServiceFactory
* @since   : 

*/
public class HttpClientAccessTokenServiceForCODEF implements HttpClientAccessTokenService {
	public static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
	
	public static final String OAUTH_ACCESS_TOKEN_URI   = "url";
	public static final String OAUTH_CLIENT_ID          = "client_id";
	public static final String OAUTH_CLIENT_SECRET      = "client_secret";
	public static final String OAUTH_SCOPE              = "scope";
	public static final String OAUTH_GRANT_TYPE         = "grant_type";
	
	public static final String TOKEN_ERROR_CHECK_KEY	= "token_error_check_key";
	public static final String TOKEN_ERROR_CHECK_VALUES	= "token_error_check_values";
	
    public AccessTokenVO execute(String name, Properties prop) throws Exception {
    	String url                   = prop.getProperty("URL");
    	String encode                = prop.getProperty("ENCODE");
    	String timeoutTemp           = prop.getProperty("HTTP_TIME_OUT", "0");
        String connectionTimeoutTemp = prop.getProperty("CONNECTION_TIMEOUT", "0");
        
        int timeout                  = Integer.parseInt(timeoutTemp);
    	int connectionTimeout        = Integer.parseInt(connectionTimeoutTemp);
    	long currentTime             = System.currentTimeMillis();
    	
    	PropManager propManager = PropManager.getInstance();
    	String tokenInfo = propManager.getProperty(AccessTokenVO.PROP_GROUP, name);
    	
    	if (StringUtils.isBlank(tokenInfo)) {
    		throw new Exception("TokenInfo is NULL");
    	}
    	
    	/** 
    	 * 프로퍼티 OAuthInfo에 프로퍼티 명이 TokenManager에서 관리하는 Key(어댑터그룹명)로 등록
         * 		interval, url, client_id, client_secret(암호화), scope, grant_type
         * 
         * ex) _TST_OU_RST_SyC =>
         * {
         *    "interval": "86400",
         *    "url": "oauth/2.0/token",
         *    "client_id": "l7xx3b02c23ae8d64d2fb12a7266895f8687",
         *    "client_secret": "4e88d1d2edb44eadb35a1f6da9823a64",
         *    "scope": "read",
         *    "grant_type": "client_credentials"
         * }
         */
		JSONObject tokenInfoObject = (JSONObject) JSONValue.parse(tokenInfo);
		
		String oauthAccessTokenUrl   = tokenInfoObject.containsKey(OAUTH_ACCESS_TOKEN_URI) ? (String)tokenInfoObject.get(OAUTH_ACCESS_TOKEN_URI) : "";
		String oauthClientId         = tokenInfoObject.containsKey(OAUTH_CLIENT_ID       ) ? (String)tokenInfoObject.get(OAUTH_CLIENT_ID       ) : "";
		String oauthClientSecret     = tokenInfoObject.containsKey(OAUTH_CLIENT_SECRET   ) ? (String)tokenInfoObject.get(OAUTH_CLIENT_SECRET   ) : "";
		String oauthScope            = tokenInfoObject.containsKey(OAUTH_SCOPE           ) ? (String)tokenInfoObject.get(OAUTH_SCOPE           ) : "";
		String oauthGrantType        = tokenInfoObject.containsKey(OAUTH_GRANT_TYPE      ) ? (String)tokenInfoObject.get(OAUTH_GRANT_TYPE      ) : "";
		
		//2020-03-12 Scp 모듈 Util로 변경
		// client_secret를 복호화한다
		try {
			oauthClientSecret = ScpDbAgentUtil.ScpDecB64(PropManager.getInstance().getProperties(ScpDbAgentUtil.EXT_MODULE), oauthClientSecret, "EUC-KR");
		} catch (Exception e) {
			logger.error("addInboundErrorInfo] scpdb_agent encryption Error - " + e.getMessage());
		}
		
		String uri = "";
		if (StringUtils.contains(oauthAccessTokenUrl, "http://")
				|| StringUtils.contains(oauthAccessTokenUrl, "https://")) {
			uri = oauthAccessTokenUrl;
		} else {
			uri = url + "/" + oauthAccessTokenUrl;
		}
		
		String contentType = propManager.getProperty(AccessTokenVO.PROP_GROUP, AccessTokenVO.HEADER_CONTENT_TYPE);
		
		HttpClient mclient = new HttpClient();
		PostMethod httpMethod = new PostMethod(uri);
		
		if(timeout > -1) {
			httpMethod.getParams().setSoTimeout(timeout);
	    }
	    
	    if ( connectionTimeout != 0) {
	    	httpMethod.getParams().setParameter("http.connection.timeout", connectionTimeout);
	    }
	    httpMethod.getParams().setParameter("http.protocol.version", HttpVersion.HTTP_1_0);
	    
	    if(uri != null && uri.startsWith("https")){
        	Protocol.registerProtocol("https", new Protocol("https",
        			new CustomHttpsSocketFactory(new EasySSLProtocolSocketFactory()), 443));
        }
		
		try {
			httpMethod.setParameter(OAUTH_GRANT_TYPE   , oauthGrantType   );
			httpMethod.setParameter(OAUTH_SCOPE        , oauthScope       ); ////
			
			String basicAuth = oauthClientId + ":" + oauthClientSecret;
			basicAuth = "Basic " + Base64.getEncoder().encodeToString(basicAuth.getBytes());
			httpMethod.setRequestHeader("Authorization", basicAuth);
			httpMethod.setRequestHeader("Content-Type", contentType + "; charset=" + encode);
			
			logger.debug("HttpClientAccessTokenServiceForCODEF] oauthClientId     = ["+ oauthClientId +"]");
			logger.debug("HttpClientAccessTokenServiceForCODEF] oauthClientSecret = ["+ oauthClientSecret +"]");
			logger.debug("HttpClientAccessTokenServiceForCODEF] oauthScope        = ["+ oauthScope +"]");
			logger.debug("HttpClientAccessTokenServiceForCODEF] oauthGrantType    = ["+ oauthGrantType +"]");
			logger.debug("HttpClientAccessTokenServiceForCODEF] contentType       = ["+ contentType +"]");
			logger.debug("HttpClientAccessTokenServiceForCODEF] encode            = ["+ encode +"]");
			logger.debug("HttpClientAccessTokenServiceForCODEF] Authorization     = ["+ basicAuth +"]");
			
	    	int status = mclient.executeMethod(httpMethod);
	    	
	    	if (status !=200) {
	    		throw new Exception ("oauth token receive status fail value= " + status);
	    	}
	    	
	    	byte[] responseBody = httpMethod.getResponseBody();
	    	String responseString = new String(responseBody, encode);
	    	
	    	OAuth2AccessTokenVO accessToken = new OAuth2AccessTokenVO();
	    	
	    	if (StringUtils.isNotBlank(responseString)) {
	    		logger.debug("HttpClientAccessTokenServiceForCODEF] oauthToken RECV = ["+ responseString +"]");
	    		
	    		JSONObject responseJSON = (JSONObject) JSONValue.parse(responseString);
	    		
	    		if (responseJSON.containsKey("access_token")
	    				&& responseJSON.containsKey("token_type")     
	    				&& responseJSON.containsKey("expires_in")     
	    				&& responseJSON.containsKey("scope")) {
	    			accessToken.setAccessToken((String) responseJSON.get("access_token"));   
	    			accessToken.setTokenType  ((String) responseJSON.get("token_type"  ));     
	    			accessToken.setExpiration (new Date(currentTime + ((long) responseJSON.get("expires_in") * 1000L)));     
	    			accessToken.setScope      ((String) responseJSON.get("scope"       ));          
	    		}
	    		
	    		if (responseJSON.containsKey("client_use_code")) {
	    			accessToken.setClientUseCode((String) responseJSON.get("client_use_code"));
	    		}
	    		
	    		logger.debug("HttpClientAccessTokenServiceForCODEF] oauthToken ="+ accessToken.toString());
	    	} else {
	    		throw new Exception ("oauth token return null");
	    	}
	    	
	    	return accessToken;
	    // 오류 처리 상세화 필요	
		} catch (Exception e) {
			logger.error("HttpClientAccessTokenServiceForCODEF] oauth token fail : " + e.toString() ,e);
			throw e;
		} finally {
			httpMethod.releaseConnection();
		}
    }
} 
