/*********************************************************************
* All Rights reserved,Copyright (c) K-Opticom
**********************************************************************
*＜プログラム内容＞
*	システム名		：eo顧客基幹システム
*	モジュール名	：JKKCtrlWaoStubImpl
*	ソースファイル名：JKKCtrlWaoStubImpl.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.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
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 JKKCtrlWaoStubImpl 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";
	
	/** ＷＡＯＡＰＩスタブ用レスポンス値格納場所 KKIFE513 */
	protected static final String KK_NETFLIX_STB_RES_KKIFE513 = "WAO_API_STB_RES_KKIFE513";
	/** ＷＡＯＡＰＩスタブ用レスポンス値格納場所 KKIFE514 */
	protected static final String KK_NETFLIX_STB_RES_KKIFE514 = "WAO_API_STB_RES_KKIFE514";
	/** ＷＡＯＡＰＩスタブ用レスポンス値格納場所 KKIFE515 */
	protected static final String KK_NETFLIX_STB_RES_KKIFE515 = "WAO_API_STB_RES_KKIFE515";
	/** ＷＡＯＡＰＩスタブ用HTTPステータス */
	protected static final String WAO_API_STB_HTTP_STATUS_KKIFE513 = "WAO_API_STB_HTTP_STATUS_KKIFE513";
	/** ＷＡＯＡＰＩスタブ用HTTPステータス */
	protected static final String WAO_API_STB_HTTP_STATUS_KKIFE514 = "WAO_API_STB_HTTP_STATUS_KKIFE514";
	/** ＷＡＯＡＰＩスタブ用HTTPステータス */
	protected static final String WAO_API_STB_HTTP_STATUS_KKIFE515 = "WAO_API_STB_HTTP_STATUS_KKIFE515";
	/** スタブ利用時でのファイル取得時の実行結果を指定 */
	private static final Map<String, String> NOT_EXITS_FILE_RESULT_MAP = new HashMap<String, String>();
	/**
	 * ＡＰＩＩＤとサービスの対応表をセットする
	 */
	static
	{
		/**
		 * APIIDとスタブファイルが存在しない場合の実行結果の対比表をセットする。
		 */
		NOT_EXITS_FILE_RESULT_MAP.put(WAO_API_KKIFE513, "{\"result_code\":\"00000000\",\"message\":\"\",\"data\":{\"actor\":{\"id\":2,\"lname\":\"\",\"fname\":\"\",\"email\":\"nishimura@nishimura.bplats.co.jp\",\"login_flg\":1}}}"); 
		NOT_EXITS_FILE_RESULT_MAP.put(WAO_API_KKIFE514, "{\"result_code\":\"00000000\",\"message\":null,\"data\":null}"); 
		NOT_EXITS_FILE_RESULT_MAP.put(WAO_API_KKIFE515, "{\"result_code\":\"00000000\",\"message\":null,\"data\":{\"order\":{\"id\":16}}}"); 
	}
	
	/**
	 *  ＷＡＯ連携処理を行います。
	 *  <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)));
			httpStatus = Integer.parseInt(JCMAPLConstMgr.getString(WAO_API_STB_HTTP_STATUS_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)));
			httpStatus = Integer.parseInt(JCMAPLConstMgr.getString(WAO_API_STB_HTTP_STATUS_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)));
			httpStatus = Integer.parseInt(JCMAPLConstMgr.getString(WAO_API_STB_HTTP_STATUS_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+", " ");
				
				// ★★★検証環境用★★★
				String res = null;
				if (httpStatus >= 200 && httpStatus < 300)
				{
					res = strToUnicode(responseParamMake(waoApiId, svcKeiNo));
				}
				if  (res != null)
				{
					response = response.append(res);
				}
				resLog = response.toString().replaceAll("\\s+", " ");
				
				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;
	}
	
	/**
	 * レスポンスパラメータを作成する。
	 * @param waoApiId ＷＡＯ社　API-ID
	 * @param svcKeiNo サービス契約番号
	 * @return JSON形式のレスポンスパラメータを返却する。
	 * @throws IOException JOSN形式へ変換する際のI/O例外
	 */
	private static String responseParamMake(String waoApiId, String svcKeiNo) throws IOException
	{
		// レスポンス値
		String responseResult = NOT_EXITS_FILE_RESULT_MAP.get(waoApiId);
		// レスポンスファイル
		String path = "";
		if (WAO_API_KKIFE513.equals(waoApiId))
		{
			path = JCMAPLConstMgr.getString(KK_NETFLIX_STB_RES_KKIFE513);
		}
		else if (WAO_API_KKIFE514.equals(waoApiId))
		{
			path = JCMAPLConstMgr.getString(KK_NETFLIX_STB_RES_KKIFE514);
		}
		else if (WAO_API_KKIFE515.equals(waoApiId))
		{
			path = JCMAPLConstMgr.getString(KK_NETFLIX_STB_RES_KKIFE515);
		}
		
		// 指定ファイルパスチェック
		if (path == null || "".equals(path.trim()))
		{
			return responseResult;
		}
		BufferedReader br = null;
		try
		{
			// レスポンスファイル読込み
			br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(JCRUtilCommon.inspection(path))), WAO_ENCODING));
			// ファイル一行読込み
			String lineData = br.readLine();
			String[] response = null;
			while (lineData != null)
			{
				if (!("".equals(lineData.trim())))
				{
					if (WAO_API_KKIFE514.equals(waoApiId) || WAO_API_KKIFE515.equals(waoApiId))
					{
						response = lineData.split("@@");
						
						// 検索キーと一致するデータを取得する
						if(response[0].equals(svcKeiNo))
						{
							responseResult = response[1];
							
							// 検索対象が存在した場合は、処理を抜ける
							break;
						}
					}
					else
					{
						// リストに格納された一行分のファイルデータを格納
						responseResult = lineData;
					}
				}
				// ファイル一行読込み
				lineData = br.readLine();
			}
		}
		catch(IOException ioe)
		{
			// 例外処理
		}
		finally
		{
			if (br != null)
			{
				try
				{
					br.close();
				}
				catch (IOException ioe)
				{
					// 例外処理
				}
			}
		}
		
		return responseResult;
	}
	
	/**
	 * 文字コードをUnicodeに変換します
	 * @param arg0 Unicodeに変換したい文字列
	 * @return Unicodeに変換された文字列
	 */
	private static String strToUnicode(String str)
	{
		if (str == null)
		{
			return str;
		}
		
		StringBuilder sb = new StringBuilder();
		for (char charStr : str.toCharArray())
		{
			if (charStr > 127)
			{
				sb.append(String.format("\\u%04x", (int)charStr));
			}
			else
			{
				sb.append(charStr);
			}
		}
		
		return sb.toString();
	}
}
