/*******************************************************************************
*	All Rights reserved,Copyright (c) K-Opticom
********************************************************************************
*＜プログラム内容＞
*	システム名		：eo顧客基幹システム
*	モジュール名	：JECNA0270001TPMA
*	ソースファイル名：JECNA0270001TPMA.java
*	作成者			：FJ）北村
*	日付			：2023年10月10日
*＜機能概要＞
*	CNIFE051_料金訂正履歴参照です。
*＜修正履歴＞
*	バージョン	修正日		修正者		修正内容
*	v53.00.00	2023/10/10	FJ）北村	【ANK-4346-00-00】【eo定期】CCMG再構築対応
********************************************************************************/
package eo.ejb.cbs.mainproc;


import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fujitsu.futurity.model.base.CAANMsg;
import com.fujitsu.futurity.model.common.JCMAPLConstMgr;
import com.fujitsu.futurity.model.ejb.common.JSYejbLog;
import com.fujitsu.futurity.model.ejb.common.StatusCodes;
import com.fujitsu.futurity.model.ejb.common.fw.AgentDispatchContext;
import com.fujitsu.futurity.model.ejb.common.fw.TemplateMainHandler;

import eo.common.constant.JCNStrConst;
import eo.ejb.cbs.cbsmsg.ECNA0270001CBSMsg;
import eo.ejb.cbs.cbsmsg.ECNA0270001CBSMsg1List;
import eo.ejb.cbs.cbsmsg.ECNA0270001CBSMsg2List;
import eo.ejb.common.JCNCtrlCcmgConnectInfoImpl;


/**
 * 課金参照独自処理部品です。
 * <br>
 * @author FJ
 *
 */
public class JECNA0270001TPMA implements TemplateMainHandler
{
	/** エラーフラグ(EA：設定値取得エラー) */
	private static final String EA_ERR_FLAG = "EA";
	
	/** CCMGへのリクエストメソッド*/
	private static final String REQ_METHOD = "GET";
		
	/** モード取得用キー */
	private static final String CCMG_MODE_KEY = "CCMG_CNIFE051_CONNECTION_MODE";
	
	/** CCMGのCNIFE051_課金参照のURL*/
	private static final String CCMG_API_URL = "CCMG_CNIFE051_URL";
	
	/** CCMGのCNIFE051_課金参照API*/
	private static final String CCMG_APIID_CNIFE051 = "CNIFE051";
	
	/** CCMGへのリクエストタイムアウト時間*/
	private static final String CCMG_API_TIMEOUT = "CCMG_CNIFE051_TIMEOUT";
	
	/** CCMGへのリトライ回数*/
	private static final String CCMG_API_RETRY_CNT = "CCMG_CNIFE051_RETRYCOUNT";
	
	/** CCMGへのリトライ間隔*/
	private static final String CCMG_API_RETRY_INTERVAL = "CCMG_CNIFE051_RETRYINTERVAL";
	
	/** ダミーHTTPステータス取得用キー */
	private static final String STUB_HTTP_STATUS_KEY = "CN_CCMG_DUMMY_HTTP_STATUS_CONTRACT";
	
	/** 本番モード */
	private static final String MODE_0 = "0";
	
	/**
	 * XML_PATH
	 */
	protected static final String CCMG_CONNECTION_MODE_TEST_DIR = "CCMG_CONNECTION_MODE_TEST_DIR";

	/** 応答コードOK */
	private static final String CODE_OK = "00000";
	
	/** 【パラメータ一覧】  */
	/** ダミーURL */
	private static final String DUMMY_URL = "DUMMY";
	
	/** CCMG実行結果 HTTPStatus */
	public static final String EXE_STATUS = "httpStatus";
	/** エラーコード */
	private static final String ERR_CD = "err_cd";
	/** API送信 項目名 */
	private static final String[][] CNIFE051_REQ_INFO = new String[][]{
		  {"contract_no",					ECNA0270001CBSMsg.KEY_CONTRACT_NO}					// コンテンツ契約番号
		, {"sysid",							ECNA0270001CBSMsg.KEY_SYSID}						// SYSID
		, {"target_date",					ECNA0270001CBSMsg.KEY_TARGET_DATE}					// 対象年月
		, {"after_corrected_division",		ECNA0270001CBSMsg.KEY_AFTER_CORRECTED_DIVISION}		// 変更後訂正区分
		, {"update_date_from",				ECNA0270001CBSMsg.KEY_UPDATE_DATE_FROM}				// データ更新日時（開始）
		, {"update_date_to",				ECNA0270001CBSMsg.KEY_UPDATE_DATE_TO}				// データ更新日時（終了）
		, {"page",							ECNA0270001CBSMsg.KEY_PAGE}							// ページ番号
		, {"per_page",						ECNA0270001CBSMsg.KEY_PER_PAGE}						// 1ページに表示する件数
		, {"sort",							ECNA0270001CBSMsg.KEY_SORT}							// ソート順
		, {"sort_order",					ECNA0270001CBSMsg.KEY_SORT_ORDER}					// 昇順/降順
	};
	
	/** API受信 項目名 */
	private static final String[][] CNIFE051_RES_LIST1_INFO = new String[][]{
		  {ERR_CD,							ECNA0270001CBSMsg1List.ERR_CD}						// エラーコード
		, {"total",							ECNA0270001CBSMsg1List.TOTAL}					// 総件数
		, {"total_page",					ECNA0270001CBSMsg1List.TOTAL_PAGE}					// 総ページ番号
		, {"page",							ECNA0270001CBSMsg1List.PAGE}						// 現在のページ番号
		, {"sysid",							ECNA0270001CBSMsg1List.SYSID}						// SYSID
		, {"correction-histories",			ECNA0270001CBSMsg1List.ECNA0270001CBSMSG2LIST}		// 課金訂正情報
	};
	/** API受信 項目名(課金訂正情報配列) */
	private static final String[][] CNIFE051_RES_LIST2_INFO = new String[][]{
		  {"contract_no",					ECNA0270001CBSMsg2List.CONTRACT_NO}					// 契約番号
		, {"target_date",					ECNA0270001CBSMsg2List.TARGET_DATE}					// 対象年月
		, {"record_no",						ECNA0270001CBSMsg2List.RECORD_NO}					// 履歴番号
		, {"operation_cd",					ECNA0270001CBSMsg2List.OPERATION_CD}				// 処理コード
		, {"before_corrected_division",		ECNA0270001CBSMsg2List.BEFORE_CORRECTED_DIVISION}	// 変更前訂正区分
		, {"before_discount_amount",		ECNA0270001CBSMsg2List.BEFORE_DISCOUNT_AMOUNT}		// 変更前値引額
		, {"before_corrected_notes",		ECNA0270001CBSMsg2List.BEFORE_CORRECTED_NOTES}		// 変更前訂正記事
		, {"after_corrected_division",		ECNA0270001CBSMsg2List.AFTER_CORRECTED_DIVISION}	// 変更後訂正区分
		, {"after_discount_amount",			ECNA0270001CBSMsg2List.AFTER_DISCOUNT_AMOUNT}		// 変更後値引額
		, {"after_corrected_notes",			ECNA0270001CBSMsg2List.AFTER_CORRECTED_NOTES}		// 変更後訂正記事
		, {"update_id",						ECNA0270001CBSMsg2List.UPDATE_ID}					// データ更新者ＩＤ
		, {"update_name",					ECNA0270001CBSMsg2List.UPDATE_NAME}					// データ更新者
		, {"update_date",					ECNA0270001CBSMsg2List.UPDATE_DATE}				// 更新日時
	};

	/**
	 * 課金参照を行います。
	 * 
	 * @param inCBSMsg 課金参照メッセージ
	 * @param inContext AgentDispatchContext
	 */
	@SuppressWarnings("unchecked")
	public static void call_CNIFE051(CAANMsg inCBSMsg, AgentDispatchContext inContext)
	{
		// 開始ログ出力を実施。
		JSYejbLog.println(JSYejbLog.DEBUG, JECNA0270001TPMA.class, "JECNA0270001TPMA#invoke start");
		
		Hashtable<String, Object> response = new Hashtable<String, Object>();
		
		/** 接続先などの判定に使用
		*   OFF： 本番・検証、ON: 開発
		*/
		String mode = JCMAPLConstMgr.getString(CCMG_MODE_KEY);
		// アプリケーションプロパティ設定チェック
		if(isNullBlank(mode))
		{
			// ステータス設定（設定値取得エラー）
			inCBSMsg.set(ECNA0270001CBSMsg.STATUS, StatusCodes.RELATION_ERR);
			inCBSMsg.set(ECNA0270001CBSMsg.KEY_SYSID_ERR, EA_ERR_FLAG);
			inCBSMsg.set(ECNA0270001CBSMsg.ECNA0270001CBSMSG1LIST, new CAANMsg[0]);
			return;
		}
		
		// eo顧客 ⇒ CCMGへの送信先URLを設定
		String strUrl = "";
		
		if (MODE_0.equals(mode))
		{
			// 本番・検証：propertiesに設定された値を使用
			strUrl = JCMAPLConstMgr.getString(CCMG_API_URL);
		}
		else
		{
			// 開発：設定しない
			strUrl = DUMMY_URL;
		}
		
		// API名称
		String apiName = CCMG_APIID_CNIFE051;
		// 接続タイムアウト
		String strConnectTimeout = JCMAPLConstMgr.getString(CCMG_API_TIMEOUT);
		// リトライ回数
		String strRetryCount = JCMAPLConstMgr.getString(CCMG_API_RETRY_CNT);
		// リトライインターバル
		String strRetryInterval = JCMAPLConstMgr.getString(CCMG_API_RETRY_INTERVAL);
		
		// アプリケーションプロパティ設定チェック
		if(isNullBlank(strUrl)
				|| isNullBlank(strConnectTimeout)
				|| isNullBlank(strRetryCount)
				|| isNullBlank(strRetryInterval))
		{
			// ステータス設定（設定値取得エラー）
			inCBSMsg.set(ECNA0270001CBSMsg.STATUS, StatusCodes.RELATION_ERR);
			inCBSMsg.set(ECNA0270001CBSMsg.KEY_SYSID_ERR, EA_ERR_FLAG);
			inCBSMsg.set(ECNA0270001CBSMsg.ECNA0270001CBSMSG1LIST, new CAANMsg[0]);
			return;
		}
		JSYejbLog.println(JSYejbLog.EXECUTION, JECNA0270001TPMA.class , "JECNA0270001TPMA#URL=" + strUrl + apiName);
		JsonNode root = null;
		Hashtable<String, Object> jsonResult = new Hashtable<String, Object>();

		try
		{
			/*
			 * 送信パラメータ作成
			 */
			CAANMsg paramPost = requestParamMake(inCBSMsg, apiName, mode, strUrl, strConnectTimeout, strRetryCount, strRetryInterval);
			JSYejbLog.println(JSYejbLog.EXECUTION, JECNA0270001TPMA.class , "JECNA0270001TPMA#request postParameter=" + inCBSMsg);
			
			// コマンド発行部品のインスタンスを取得
			JCNCtrlCcmgConnectInfoImpl connection = new JCNCtrlCcmgConnectInfoImpl();
			response = connection.ctrlCcmgConnect(paramPost);
			
			// 接続結果
			Map<String, Object> connectResult = (Map<String, Object>) response.get("connectResult");
			// レスポンス情報
			Map<String, Object> resInfo = (Map<String, Object>) response.get("resInfo");
			// レスポンス情報
			Object resultBody = resInfo.get("body");
			
			// body情報マッピング
			ObjectMapper mapper = new ObjectMapper();
			root = mapper.readTree(resultBody.toString());
			jsonResult = read(root);
			
			// 連携実行結果判定
			if(isNormal(connectResult,root))
			{
				// ステータス設定（0:正常終了）
				inCBSMsg.set(ECNA0270001CBSMsg.STATUS, StatusCodes.NORMAL_END);
			}
			else
			{
				// ステータス設定（応答コードエラー）
				inCBSMsg.set(ECNA0270001CBSMsg.STATUS, StatusCodes.EXTERNAL_IF_ERR1);
			}

		}
		catch (Exception e)
		{
			printErrorLog(e);
			// ステータス設定（実行時エラー）
			inCBSMsg.set(ECNA0270001CBSMsg.STATUS, StatusCodes.RELATION_ERR);
			inCBSMsg.set(ECNA0270001CBSMsg.ECNA0270001CBSMSG1LIST, new CAANMsg[0]);
			return;
		}

		// JSONレスポンス
		editOutMsg(inCBSMsg, jsonResult);

		// 終了ログ出力を実施。
		JSYejbLog.println(JSYejbLog.DEBUG, JECNA0270001TPMA.class, "JECNA0270001TPMA#invoke end");
	}

	/**
	 * エラーログ出力
	 * @param e 発生した例外
	 * @param params 指示書パラメータ(1〜50)
	 * @param errTitle エラータイトル
	 * @param sjishoNo 指示書番号
	 * @param sjishoNaiyoSeq 指示書内容枝番
	 */
	static private void printErrorLog(Exception e)
	{
		StringBuffer sb = new StringBuffer();
		sb.append(e.getMessage()+ "\n");
		sb.append(e.getClass().getName()+ "\n");
		StackTraceElement[] steAll = e.getStackTrace();
		for (StackTraceElement ste : steAll) {
			sb.append("	at " + ste.getClassName() + "." + ste.getMethodName() + "(" + ste.getFileName() + ":" + ste.getLineNumber()+")"+ "\n");
		}
		JSYejbLog.println(JSYejbLog.ERROR, JECNA0270001TPMA.class, sb.toString());
	}
	
	/**
	 * 連携実行結果判定
	 */
	private static boolean isNormal(Map<String, Object> connectResult, JsonNode root) 
	{
		if(connectResult==null)
		{
			return false;
		}
	
		// HTTPステータスをログ出力
		JSYejbLog.println(JSYejbLog.EXECUTION, JECNA0270001TPMA.class, "JECNA0270001TPMA#http_status=" + connectResult.get(EXE_STATUS) );
		
		// HTTPステータス確認
		if( !Integer.toString(HttpURLConnection.HTTP_OK).equals(connectResult.get(EXE_STATUS)))
		{
			return false;
		}
		
		// エラーコードがbody部にあるので確認
		// 応答コード（00000:正常）の確認
		if (!CODE_OK.equals(root.get(ERR_CD).textValue()))
		{
			return false;
		}
		return true;
	}

	/**
	 * 課金訂正履歴参照APIに対して送信するリクエストパラメータを作成する。
	 * @param inCBSMsg CAANメッセージ
	 * @param apiName API名称
	 * @param mode 処理区分
	 * @param strUrl URL
	 * @param strConnectTimeout コネクションタイムアウト
	 * @param strRetryCount リトライ回数
	 * @param strRetryInterval リトライインターバル
	 * @return リクエストパラメータを返却する。
	 */
	private static CAANMsg requestParamMake(CAANMsg inCBSMsg, String apiName, String mode, String strUrl, 
						String strConnectTimeout, String strRetryCount, String strRetryInterval)
	{
		// リクエストパラメータ作成
		CAANMsg paramPost = new CAANMsg();
		HashMap<String,Object> reqInfo = new HashMap<String, Object>();

		// リクエストパラメータヘッダー部作成
		Map<String, Object> paramHeader = new HashMap<String, Object>();
		
		paramHeader.put(JCNCtrlCcmgConnectInfoImpl.CONNECTINFO_CONNECTMODE, mode);
		paramHeader.put(JCNCtrlCcmgConnectInfoImpl.CONNECTINFO_URL, strUrl);
		paramHeader.put(JCNCtrlCcmgConnectInfoImpl.CONNECTINFO_APIID, apiName);
		paramHeader.put(JCNCtrlCcmgConnectInfoImpl.CONNECTINFO_CONNECTMETHOD, REQ_METHOD);
		paramHeader.put(JCNCtrlCcmgConnectInfoImpl.CONNECTINFO_TIMEOUT, strConnectTimeout);
		paramHeader.put(JCNCtrlCcmgConnectInfoImpl.CONNECTINFO_RETRYCOUNT, strRetryCount);
		paramHeader.put(JCNCtrlCcmgConnectInfoImpl.CONNECTINFO_RETRYINTERVAL, strRetryInterval);


		// リクエストパラメータボディ部作成
		Map<String, Object> paramBody = new HashMap<String, Object>();

			for(String[] item : CNIFE051_REQ_INFO)
		{
			String itemName = item[0];
			String itemValue = inCBSMsg.getString(item[1]);
			
			if( !isNullBlank(itemValue) )
			{
				paramBody.put(itemName, itemValue);
			}
		}
		reqInfo.put(JCNCtrlCcmgConnectInfoImpl.RESINFO_BODY,paramBody);


		// ------------------------------------
		// リストをデータマップに設定する
		// ------------------------------------
		paramPost.setPrivate("connectInfo",(Object)paramHeader);
		paramPost.setPrivate("reqInfo",(Object)reqInfo);
		return paramPost;
	}

	
	/**
	 * レスポンス電文を設定します。
	 */
	private static void editOutMsg(CAANMsg inCBSMsg, Hashtable<String, Object> response)
	{
		if (response != null)
		{
			List<CAANMsg> msgList = new ArrayList<CAANMsg>();
			CAANMsg msg = new CAANMsg(ECNA0270001CBSMsg1List.class.getName());
			for(String[] item : CNIFE051_RES_LIST1_INFO)
			{
				String itemName = item[1];
				Object itemValue = response.get(item[0]);

				if(itemValue == null)
				{
					msg.setNull(itemName);
				}
				else if (itemName.equals(ECNA0270001CBSMsg1List.ECNA0270001CBSMSG2LIST))
				{
					List<CAANMsg> msg1List = new ArrayList<CAANMsg>();
					List<Map<String, Object>> list1 = (List<Map<String, Object>>) itemValue;

					for(Map<String, Object> record1 : list1)
					{
						CAANMsg msg1 = new CAANMsg(ECNA0270001CBSMsg2List.class.getName());
						for(String[] item1 : CNIFE051_RES_LIST2_INFO)
						{
							String itemName1 = item1[1];
							Object itemValue1 = record1.get(item1[0]);
							msg1.set(itemName1, (String) itemValue1);
						}
						msg1List.add(msg1);
					}
					msg.set(itemName, msg1List.toArray(new CAANMsg[msg1List.size()]));
				}
				else
				{
					msg.set(itemName, (String)itemValue);
				}
			}
			msgList.add(msg);
			inCBSMsg.set(ECNA0270001CBSMsg.ECNA0270001CBSMSG1LIST,	msgList.toArray(new CAANMsg[msgList.size()]));
		}
	}

	/**
	 * null又は空文字の場合、trueを返却する
	 * 
	 * @param str 検査対象文字列
	 * @return true:null、又は空文字／false:左記以外
	 */
	public static boolean isNullBlank(String str)
	{
		if (str == null || "".equals(str))
		{
			return true;
		}
		return false;
	}
	
	/**
	 * 電文読み込み処理
	 * @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();
			
			
			if(fieldValue.isNull())
			{
				//何もしない
			}
			// Arrayの場合
			else if(fieldValue.isArray())
			{
				ArrayList<Object> Bodynodelist = new ArrayList<Object>();
				Iterator<JsonNode> itr = fieldValue.elements();
				while(itr.hasNext())
				{
					JsonNode node = itr.next();
					if(node.isValueNode())
					{
						Bodynodelist.add(node.textValue());
					}
					else
					{
						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
			{
				if(fieldValue.isInt())
				{
					resultMap.put(fieldName, fieldValue.bigIntegerValue().toString());
				}
				else if(fieldValue.isTextual())
				{
					resultMap.put(fieldName, fieldValue.textValue());
				}
				else
				{
					//何もしない
				}
			}
		}
		
		return resultMap;
	}
	
	/**
	 * 引数で受け取った値がNULLの場合、空を設定し返却する。
	 * <br>
	 * @param Object 
	 */
	private static Object objNullToBlank(Object obj)
	{
		if(obj == null)
		{
			return JCNStrConst.S_BLANK;
		}
			return obj;
	}
}
