/*********************************************************************
* All Rights reserved,Copyright (c) K-Opticom
**********************************************************************
*＜プログラム内容＞
*	システム名		：eo顧客基幹システム
*	モジュール名	：JKKCtrlWaoImpl
*	ソースファイル名：JKKCtrlWaoImpl.java
*	作成者			：FJ
*	日付			：2025年04月09日
*＜機能概要＞
*	ＷＡＯ社 外部連携（本番環境用）コマンド発行部品です。
*＜修正履歴＞
*	バージョン	修正日		修正者		修正内容
*	v75.00.00   2025/04/09  FJ)謝       【ANK-4640-00-00】typeN：追加要件対応
*
**********************************************************************/
package eo.common.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;

import javax.net.ssl.HttpsURLConnection;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fujitsu.futurity.model.common.JCMAPLConstMgr;


/**
 * ＷＡＯ連携独自処理部品（本番環境用）です。
 * @author FJ
 */
public class JKKCtrlWaoImpl extends JKKCtrlWao
{
	/** ＷＡＯ社　API-ID KKIFE513 管理者情報認証取得(WAO) */
	private static final String WAO_API_KKIFE513 = "KKIFE513";
	/** ＷＡＯ社　API-ID KKIFE514 注文チェック(WAO) */
	private static final String WAO_API_KKIFE514 = "KKIFE514";
	/** ＷＡＯ社　API-ID KKIFE515 注文登録(WAO) */
	private static final String WAO_API_KKIFE515 = "KKIFE515";
	
	/** 【INPUT】 */
	private static final String API_ID = "api_id";
	private static final String SVC_KEI_NO = "svc_kei_no";
	private static final String REQUEST = "request";
	
	/** 【OUTPUT】 */
	private static final String REQ_HEADER = "req_header";
	private static final String REQUEST_JSON = "request_json";
	private static final String RESPONSE_JSON = "response_json";
	private static final String HTTP_STATUS_CD = "http_status_cd";
	private static final String RESPONSE = "response";
	
	/** 通信方式SSL */
	private static final String S_HTTPS = "https";
	
	/** 【HTTPヘッダ】  */
	/** CONTENT_TYPE  */
	private static final String CONTENT_TYPE = "Content-Type";

	/** CONTENT_TYPE_設定値  */
	private static final String CONTENT_TYPE_VALUE = "application/json";
	
	/** Authorization_設定値  */
	private static final String ACCESSKEY = "accesskey";

	/** WAO文字コード */
	private static final String WAO_ENCODING = "UTF-8";

	/** ＷＡＯ社　リトライカウント KKIFE513 */
	protected static final String WAO_RETRYCOUNT_KKIFE513 = "WAO_RETRYCOUNT_KKIFE513";
	/** ＷＡＯ社　リトライカウント KKIFE514 */
	protected static final String WAO_RETRYCOUNT_KKIFE514 = "WAO_RETRYCOUNT_KKIFE514";
	/** ＷＡＯ社　リトライカウント KKIFE515 */
	protected static final String WAO_RETRYCOUNT_KKIFE515 = "WAO_RETRYCOUNT_KKIFE515";
	
	/** ＷＡＯ社　タイムアウト時間 KKIFE513 */
	protected static final String WAO_TIMEOUT_KKIFE513 = "WAO_TIMEOUT_KKIFE513";
	/** ＷＡＯ社　タイムアウト時間 KKIFE514 */
	protected static final String WAO_TIMEOUT_KKIFE514 = "WAO_TIMEOUT_KKIFE514";
	/** ＷＡＯ社　タイムアウト時間 KKIFE515 */
	protected static final String WAO_TIMEOUT_KKIFE515 = "WAO_TIMEOUT_KKIFE515";
	
	/** ＷＡＯ社　コネクションタイムアウト時間 */
	protected static final String WAO_CONNECTION_TIMEOUT = "WAO_CONNECTION_TIMEOUT";
	
	/** ＷＡＯＡＰＩリトライインターバル */
	protected static final String WAO_RETRYINTERVAL = "WAO_RETRYINTERVAL";
	
	/** ＷＡＯ社　プロキシホスト */
	protected static final String WAO_HTTPS_PROXY_HOST = "WAO_HTTPS_PROXY_HOST";
	/** ＷＡＯ社　プロキシポート */
	protected static final String WAO_HTTPS_PROXY_PORT = "WAO_HTTPS_PROXY_PORT";
	
	/** ＷＡＯ社　接続先URL KKIFE513 */
	protected static final String WAO_URL_KKIFE513 = "WAO_URL_KKIFE513";
	/** ＷＡＯ社　接続先URL KKIFE514 */
	protected static final String WAO_URL_KKIFE514 = "WAO_URL_KKIFE514";
	/** ＷＡＯ社　接続先URL KKIFE515 */
	protected static final String WAO_URL_KKIFE515 = "WAO_URL_KKIFE515";
	
	/** ＷＡＯＡＰＩ接続用トークン */
	protected static final String WAO_ACCESSTOKEN = "WAO_ACCESSTOKEN";
	
	/**
	 *  ＷＡＯ連携処理を行います。
	 *  <br>
	 * @param serviceMap ＷＡＯ連携のインターフェイス情報
	 * @return 処理結果コード値　0：正常　1:異常
	 */
	public Map<String, Object> call_wao(Map<String, Object> serviceMap) throws Exception
	{
		// 電文送信処理の実行
		HashMap<String, Object> result = execute(serviceMap);
		
		return result;
	}
	
	/**
	 * JSON電文送信処理を実行する。
	 * 
	 * @param serviceMap 管理者情報認証取得・注文チェック・注文登録のインターフェイス情報
	 * @return 処理結果コード値　0：正常　1:異常
	 * @throws Exception 
	 */
	@SuppressWarnings("unchecked")
	private HashMap<String, Object> execute(Map<String, Object> serviceMap) throws Exception 
	{
		HashMap<String, Object> result = new HashMap<String, Object>();
		Hashtable<String, Object> responseMap = new Hashtable<String, Object>();
		int httpStatus = 500;
		Exception ex = null;
		String waoApiId = "";
		String svcKeiNo = "";
		Map<String, Object> requestMap = new HashMap<String, Object>();
		String reqLog = "";
		String resLog = "";
		String reqHeader = "";
		
		int retryCount = 0;
		String urlString = "";
		Integer timeout = 0;
		
		if (serviceMap != null)
		{
			if (serviceMap.containsKey(API_ID))
			{
				waoApiId = (String)serviceMap.get(API_ID);
			}
			
			if (serviceMap.containsKey(SVC_KEI_NO))
			{
				svcKeiNo = (String)serviceMap.get(SVC_KEI_NO);
			}
			
			if (serviceMap.containsKey(REQUEST))
			{
				requestMap = (HashMap<String, Object>)serviceMap.get(REQUEST);
			}
		}
		
		if (WAO_API_KKIFE513.equals(waoApiId))
		{
			retryCount = Integer.parseInt(JCMAPLConstMgr.getString(WAO_RETRYCOUNT_KKIFE513));
			urlString = JCMAPLConstMgr.getString(WAO_URL_KKIFE513);
			timeout = (Integer.parseInt(JCMAPLConstMgr.getString(WAO_TIMEOUT_KKIFE513)));
		}
		else if (WAO_API_KKIFE514.equals(waoApiId))
		{
			retryCount = Integer.parseInt(JCMAPLConstMgr.getString(WAO_RETRYCOUNT_KKIFE514));
			urlString = JCMAPLConstMgr.getString(WAO_URL_KKIFE514);
			timeout = (Integer.parseInt(JCMAPLConstMgr.getString(WAO_TIMEOUT_KKIFE514)));
		}
		else if (WAO_API_KKIFE515.equals(waoApiId))
		{
			retryCount = Integer.parseInt(JCMAPLConstMgr.getString(WAO_RETRYCOUNT_KKIFE515));
			urlString = JCMAPLConstMgr.getString(WAO_URL_KKIFE515);
			timeout = (Integer.parseInt(JCMAPLConstMgr.getString(WAO_TIMEOUT_KKIFE515)));
		}
		
		// urlが指定されていない場合は終了
		if (urlString == null || urlString.length() == 0)
		{
			return result;
		}
		
		String strPrxyHost = JCMAPLConstMgr.getString(WAO_HTTPS_PROXY_HOST);
		String strPrxyPort = JCMAPLConstMgr.getString(WAO_HTTPS_PROXY_PORT);
		String strAccesstoken = JCMAPLConstMgr.getString(WAO_ACCESSTOKEN);
		Integer conectTimeout = (Integer.parseInt(JCMAPLConstMgr.getString(WAO_CONNECTION_TIMEOUT)));
		Integer retryInterval = (Integer.parseInt(JCMAPLConstMgr.getString(WAO_RETRYINTERVAL)));
		
		retryCount += 1;
		
		for(int i = 0 ; i < retryCount ; i++)
		{
			HttpURLConnection connection = null;
			URL url = null;
			StringBuffer response = new StringBuffer();
			ex = null;
			
			try
			{
				url = new URL(urlString);
				
				// 電文を送信
				// 通信方式判定
				boolean bSslFlg = false;
				if(urlString.startsWith(S_HTTPS))
				{
					bSslFlg = true;
				}
				
				// プロキシの設定
				int intProxyPort = 0;
				if(strPrxyHost != null)
				{
					intProxyPort = Integer.parseInt(strPrxyPort);
				}
				
				// 電文を送信
				// SSLの場合、HttpsURLConnectionへキャスト
				if(bSslFlg)
				{
					// Proxy設定値がある場合はProxyをセットしてコネクションをオープン
					if(strPrxyHost != null && strPrxyPort != null)
					{
						Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(strPrxyHost, intProxyPort));
						connection = (HttpsURLConnection) url.openConnection(proxy);
					}
					else
					{
						connection = (HttpsURLConnection) url.openConnection();
					}
					
				}
				else
				{
					// Proxy設定値がある場合はProxyをセットしてコネクションをオープン
					if(strPrxyHost != null && strPrxyPort != null)
					{
						Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(strPrxyHost, intProxyPort));
						connection = (HttpURLConnection) url.openConnection(proxy);
					}
					else
					{
						connection = (HttpURLConnection) url.openConnection();
					}
				}
				
				connection.setDoInput(true);
				connection.setDoOutput(true);
				
				// ＷＡＯ社との通信におけるタイムアウト値
				connection.setConnectTimeout(conectTimeout);
				connection.setReadTimeout(timeout);
				// HTTPリクエストコード
				connection.setRequestMethod("POST");
				// HTTPヘッダの設定
				connection.setRequestProperty(CONTENT_TYPE, CONTENT_TYPE_VALUE);
				connection.setRequestProperty(ACCESSKEY, strAccesstoken);
				// ヘッダーを文字列で取得
				reqHeader = connection.getRequestProperties().toString();
				
				// JSON形式の文字列を作成する。
				String json = requestParamMake(requestMap);
				reqLog = json.replaceAll("\\s+", " ");
				
				OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), WAO_ENCODING);
				out.write(json);
				out.flush();
				
				// 接続
				connection.connect();
				
				httpStatus = connection.getResponseCode();
				
				InputStream in = null ;
				// 通信に成功したテキストを取得する
				if (httpStatus >= 200 && httpStatus < 300)
				{
					in = connection.getInputStream();
				}
				else
				{
					in = connection.getErrorStream();
				}
				String encoding = connection.getContentEncoding();
				if (null == encoding)
				{
					encoding = WAO_ENCODING;
				}
				
				if (in != null)
				{
					final InputStreamReader inReader = new InputStreamReader(in, encoding);
					final BufferedReader bufReader = new BufferedReader(inReader);
					String line = null;
					
					while ((line = bufReader.readLine()) != null)
					{
						response.append(line);
					}
					resLog = response.toString().replaceAll("\\s+", " ");
					bufReader.close();
					inReader.close();
					in.close();
					
					if ( response.length() > 0 )
					{
						ObjectMapper mapper = new ObjectMapper();
						JsonNode root = mapper.readTree(response.toString());
						responseMap = read(root);
					}
				}
			}
			catch (Exception e)
			{
				e.printStackTrace();
				ex = e;
				StringWriter sw = new StringWriter();
				ex.printStackTrace(new PrintWriter(sw));
				resLog = sw.toString();
			}
			finally
			{
				result.put(REQ_HEADER, reqHeader);
				result.put(REQUEST_JSON, reqLog);
				result.put(RESPONSE_JSON, resLog);
				result.put(HTTP_STATUS_CD, new Integer(httpStatus).toString());
				result.put(RESPONSE, responseMap);
				if (connection != null)
				{
					//切断処理
					connection.disconnect();
					connection = null;
				}
			}
			// 失敗時はリトライ回数の上限までリトライ
			if (((ex != null) || !(httpStatus >= 200 && httpStatus < 300)) && (i < retryCount))
			{
				try
				{
					Thread.sleep(retryInterval);
				}
				catch (InterruptedException e)
				{
					// 割り込み要求による例外は無視
				}
				continue;
			}
			else
			{
				break;
			}
		}
		
		return result;
	}
	
	/**
	 * WAO APIに対して送信するリクエストパラメータを作成する。
	 * @param Map<String, Object> 入力パラメータ
	 * @return WAO JSON形式のリクエストパラメータを返却する。
	 * @throws IOException JOSN形式へ変換する際のI/O例外
	 */
	private static String requestParamMake(Map<String, Object> requestMap) throws IOException
	{
		// 変換後文字列
		String json = "";
		ObjectMapper mapper = new ObjectMapper();
		mapper.enable(SerializationFeature.INDENT_OUTPUT);
		try
		{
			// JOSN形式へ変換
			json = mapper.writeValueAsString(requestMap);
		}
		catch (IOException e)
		{
			e.printStackTrace();
			throw e;
		}
		
		return json;
	}
	
	/**
	 * 電文読み込み処理
	 * @param jsonResponse
	 * @return json電文解析結果
	 */
	private static Hashtable<String, Object> read(JsonNode jsonResponse) 
	{
		if(jsonResponse == null)
		{
			return null;
		}
		Hashtable<String, Object> result = analyzeJson(jsonResponse);

		return result;
	}
	
	/**
	 * コマンド送信戻り電文解析処理
	 * @param elem コマンド解析電文rootjson要素
	 * @param rootflag ルートフラグ
	 * @return json解析結果
	 */
	private static Hashtable<String, Object> analyzeJson(JsonNode elem)
	{
		
		Hashtable<String, Object> resultMap = new Hashtable<String, Object>();
		
		Iterator<Map.Entry<String, JsonNode>> fields = elem.fields();
		while(fields.hasNext())
		{
			Map.Entry<String, JsonNode> field = fields.next();
			
			String fieldName = field.getKey();
			JsonNode fieldValue = field.getValue();
			
			// Arrayの場合
			if(fieldValue.isArray())
			{
				ArrayList<Map<String, Object>> Bodynodelist = new ArrayList<Map<String, Object>>();
				Iterator<JsonNode> itr = fieldValue.elements();
				while(itr.hasNext())
				{
					JsonNode node = itr.next();
					Hashtable<String, Object> resultinfo = analyzeJson(node);
					if(resultinfo != null && !resultinfo.isEmpty())
					{
						Bodynodelist.add(resultinfo);
					}
				}
				
				resultMap.put( fieldName, Bodynodelist);
			}
			// 子要素を持つ場合は再帰処理
			else if( fieldValue.isObject() )
			{
				resultMap.put(fieldName, analyzeJson(fieldValue) );
			}
			// 以外は値を設定
			else
			{
				resultMap.put(fieldName, fieldValue.asText());
			}
		}
		return resultMap;
	}
}
