/*******************************************************************************
 *	All Rights reserved,Copyright (c) K-Opticom
 ********************************************************************************
 *＜プログラム内容＞
 *	システム名		：eo顧客基幹システム
 *	モジュール名	：JKKejbExclusiveProcKK0441
 *	ソースファイル名：JKKejbExclusiveProcKK0441.java
 *	作成者			：富士通
 *	日付			：2011年11月28日
 *＜機能概要＞
 *	請求オプションサービス契約を起点とするルートの排他処理部品。
 *＜修正履歴＞
 *	バージョン	修正日		修正者		修正内容
 *	ｖ1.00.00	2011/11/28	富士通		新規作成
 *
 ********************************************************************************/

package eo.ejb.common.db;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;

import com.fujitsu.futurity.model.base.CAANConnectionMgr;
import com.fujitsu.futurity.model.base.CAANJDBCUtil;
import com.fujitsu.futurity.model.base.CAANMsg;
import com.fujitsu.futurity.model.base.CAANRuntimeException;
import com.fujitsu.futurity.model.ejb.common.JSYejbConnection;
import com.fujitsu.futurity.model.ejb.common.JSYejbLog;

import eo.ejb.cbm.entity.KK0441ETMsg;
import eo.ejb.cbm.entity.KK0491ETMsg;
import eo.ejb.cbm.entity.KK1681ETMsg;
import eo.ejb.common.JKKModelCommon;

/**
 * <p>
 * 請求オプションサービス契約を起点とするルートの排他処理部品です。
 * </p>
 * @author 富士通
 *
 */
public class JKKejbExclusiveProcKK0441
{

	
	/** 更新用キー項目結合用文字（,） */
	private static final String KEY_JOIN_WORD = ",";

	/**
	 * 更新系インターフェイス使用時の処理を行います（請求オプションサービス契約番号指定）。
	 * レコードのロック、タイムスタンプの実施、ロック対象レコードの更新を行います。
	 * @param inETMsg 処理内で使用する項目を保持しているメッセージ
	 * @param key 請求オプションサービス契約番号
	 * @param updDtmBf タイムスタンプチェック用更新年月日時分秒（更新前）
	 * @param funcCd 機能コード
	 * @return ロック失敗の場合、タイムスタンプ不一致の場合はfalseを返却する。
	 */
	public boolean isExProcSeiopNo(CAANMsg inETMsg, String key, String updDtmBf, String funcCd)
	{
		JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "call:JKKejbExclusiveProcKK0441#isExProcSeiopNo");

		// 請求オプションサービス契約番号が設定されていない場合はtrueを返却する
		if(null == key)
		{
			return true;
		}
		
		// 更新年月日時分秒（更新前）が設定されていない場合はtrueを返却する
		if(null == updDtmBf)
		{
			return true;
		}
		
		// 請求オプションサービス契約番号より請求契約番号リストを取得
		ArrayList<String> seikyList = getSeikyOp(key, JKKModelCommon.getOpeDate(inETMsg));
		
		//データが取得できなかった場合はtrueを返却する
		if(seikyList.size() < 1)
		{
			return true;
		}

		// 処理対象の請求契約番号リストを取得
		ArrayList<String> tgtList = new ArrayList<String>();
		
		for (int i = 0; i < seikyList.size(); i++)
		{
			ArrayList<String> tmpList = getLockTgt(inETMsg, seikyList.get(i));

			if(tmpList.size() != 0)
			{
				//検索結果をロック対象リストに反映
				setSeiky(tgtList, tmpList);
			}
		}

		// ロック対象をログに出力する
		outLogLockList(tgtList);
		
		ArrayList<String> updArray = new ArrayList<String>();
		
		ArrayList<String> keyArrayKK0491 = new ArrayList<String>();
		
		// レコードロックの実行
		try 
		{
			for(int i = 0; i < tgtList.size(); i++)
			{
				
				// 処理対象の請求契約番号の最終更新年月日時分秒のレコードを取得
				ArrayList<HashMap<String, String>> resultList = getLastUpdRecordKK0491(tgtList.get(i));
				if(resultList == null)
				{
					return true;
				}
				
				for(HashMap<String, String> hm : resultList)
				{
					// 請求契約をロック
					CAANMsg msg = new CAANMsg(KK0491ETMsg.class.getName());
					msg.set(KK0491ETMsg.SEIKY_KEI_NO, hm.get(KK0491ETMsg.SEIKY_KEI_NO));
					msg.set(KK0491ETMsg.GENE_ADD_DTM, hm.get(KK0491ETMsg.GENE_ADD_DTM));
					new JKKejbDBAUtil(msg).findByKeyForUpdate(msg);
					
					// キー情報の格納
					StringBuffer primary = new StringBuffer();
					primary.append(hm.get(KK0491ETMsg.SEIKY_KEI_NO));
					primary.append(KEY_JOIN_WORD);
					primary.append(hm.get(KK0491ETMsg.GENE_ADD_DTM));
					keyArrayKK0491.add(primary.toString());
					
					// 更新年月日時分秒の格納
					updArray.add(hm.get(KK0491ETMsg.UPD_DTM));
				}
			}
		}
		catch (CAANRuntimeException ce)
		{
			// レコードロックに失敗した場合はfalseを返却する
			return false;
		}

		// データが取得できなかった場合はtrueを返却する
		if(keyArrayKK0491.size() < 1)
		{
			return true;
		}

		// 共通処理のインスタンスを生成
		JKKejbExclusiveProcCommon epc = new JKKejbExclusiveProcCommon();

		// タイムスタンプチェックを行い、チェックエラーの場合はfalseを返却する。
		if (!epc.isTimeStampCheck(updArray, updDtmBf))
		{
			return false;
		}

		// 機能コードが実行モードの場合はロック対象レコード更新処理を行う
		if (!epc.isFuncMode(funcCd))
		{
			epc.updateKK0491(inETMsg, keyArrayKK0491);
		}

		return true;
	}
	
	/**
	 * 照会系インターフェイス使用時の処理を行います（請求オプションサービス契約番号指定）。
	 * 指定されたキーに紐付く親テーブルを各ルートから検索し、
	 * その中で最大の更新年月日時分秒を取得します。
	 * @param inMsg 処理対象のメッセージ
	 * @param key サービス契約回線内訳番号
	 * @return 指定したキーに紐付く最終更新年月日時分秒。
	 */
	public String getLastUpdDtmSeiopsvcKeiNo(CAANMsg inMsg, String key)
	{
		JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "call:JKKejbExclusiveProcKK0441#getLastUpdDtmSeiopsvcKeiNo");
		JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), "call:JKKejbExclusiveProcKK0441#key=" + key);
		

		// 請求オプションサービス契約番号より請求契約番号リストを取得
		ArrayList<String> seikyList = getSeikyOp(key, JKKModelCommon.getOpeDate(inMsg));
		
		//データが取得できなかった場合はnullを返却する
		if(seikyList.size() < 1)
		{
			return null;
		}

		// 処理対象の請求契約番号リストを取得
		ArrayList<String> tgtList = new ArrayList<String>();
		
		for(int i = 0; i < seikyList.size(); i++)
		{
			ArrayList<String> tmpList = getLockTgt(inMsg, seikyList.get(i));

			if(tmpList.size() != 0)
			{
				//検索結果を対象リストに反映
				setSeiky(tgtList, tmpList);
			}
		}

		// 処理対象をログに出力する
		outLogLockList(tgtList);
		
		ArrayList<String> updArray = new ArrayList<String>();
		
		for(int i = 0; i < tgtList.size(); i++)
		{
			ArrayList<HashMap<String, String>> resultList = getLastUpdRecordKK0491(tgtList.get(i));
			for(HashMap<String, String> hm : resultList)
			{
				updArray.add(hm.get(KK0491ETMsg.UPD_DTM));
			}
		}

		// データが取得できなかった場合はnullを返却する
		if(updArray.size() < 1)
		{
			return null;
		}

		// 最終更新年月日時分秒を取得する
		String maxUpdDtm = new JKKejbExclusiveProcCommon().getLastUpdDtm(updArray);

		return maxUpdDtm;
	}

	/**
	 * 請求オプションサービス契約番号に紐付く、請求契約番号を取得します。
	 * @param keyOp 請求オプションサービス契約番号
	 * @param opeDate 運用日付
	 * @return データ取得用のSQL文。
	 */
	private ArrayList<String> getSeikyOp(String keyOp, String opeDate)
	{
		
		// コネクション
		Connection con = null;
		// プリペアステートメント
		PreparedStatement pstmt = null;
		// リザルトセット
		ResultSet rsltQuery = null;
		// SQL文
		StringBuffer sbSql = new StringBuffer();

		sbSql.append(" SELECT ");
		sbSql.append("    KK0441.SEIKY_KEI_NO ");
		sbSql.append(" FROM ");
		sbSql.append("    KK_T_SEIOPSVC_KEI KK0441 ");
		sbSql.append(" WHERE   KK0441.SEIOPSVC_KEI_NO = ? ");
		sbSql.append(" AND (KK0441.SEIOPSVC_KEI_NO, KK0441.RSV_APLY_YMD || KK0441.GENE_ADD_DTM) = ");
		sbSql.append("     (SELECT  KK0441_GENE.SEIOPSVC_KEI_NO, MAX(KK0441_GENE.RSV_APLY_YMD || KK0441_GENE.GENE_ADD_DTM) AS KK0441_MAX ");
		sbSql.append("      FROM    KK_T_SEIOPSVC_KEI KK0441_GENE ");
		sbSql.append("      WHERE   KK0441_GENE.SEIOPSVC_KEI_NO = KK0441.SEIOPSVC_KEI_NO ");
		sbSql.append("      AND     KK0441_GENE.RSV_APLY_YMD <= ? ");
		sbSql.append("      AND     KK0441_GENE.RSV_APLY_CD = '2' ");
		sbSql.append("      AND     KK0441_GENE.MK_FLG = '0' ");
		sbSql.append("      GROUP BY KK0441_GENE.SEIOPSVC_KEI_NO) ");

		try
		{
			// 対象テーブルのコネクション取得
			con = JSYejbConnection.getConnection(KK0441ETMsg.getTableName());

			// prepareStatementにSQL文をセット
			pstmt = con.prepareStatement(sbSql.toString());

			// ログ出力(SQL文の出力)
			JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), sbSql);

			// バインド変数設定
			CAANJDBCUtil.setParam(pstmt, 1, keyOp);
			CAANJDBCUtil.setParam(pstmt, 2, opeDate);

			// ResultSetの取得
			rsltQuery = pstmt.executeQuery();

			// 返却領域の生成
			ArrayList<String> rsltList = new ArrayList<String>();
			
			while (rsltQuery.next())
			{
				// 結果をリストに格納
				rsltList.add(rsltQuery.getString(1));
			}

			// 取得した情報を返却
			return rsltList;
		}
		catch (SQLException e)
		{
			throw new CAANRuntimeException(e);
		}
		finally
		{
			// 資源の解放
			try
			{
				if (rsltQuery != null)
				{
					rsltQuery.close();
				}
				if (pstmt != null)
				{
					pstmt.close();
				}
				if (con != null)
				{
					CAANConnectionMgr.getInstance().close(con);
				}
			}
			catch (SQLException e1)
			{
				throw new CAANRuntimeException(e1);
			}
		}
		
	}
	
	/**
	 * ロック対象キーの取得を行います。
	 * @param inMsg 処理対象のメッセージ
	 * @param key 請求契約番号
	 * @return ロック対象の請求契約番号リスト
	 */
	private ArrayList<String> getLockTgt(CAANMsg inMsg, String key)
	{
		ArrayList<String> retList = new ArrayList<String>();
		
		// keyを結果リストに詰める
		retList.add(key); 

		for(int i = 0; i < retList.size(); i++)	
		{	
			// ロック対象リストに設定された請求契約番号に異動予約の以下の項目に対して検索を行う。
			// @請求契約番号に対する検索
			// A併合先請求契約番号に対する検索
			// B分割先請求契約番号に対する検索
			ArrayList<HashMap<String, String>> resultList = searchSeikyKeiNo(retList.get(i), JKKModelCommon.getOpeDate(inMsg));
			
			if(resultList != null)
			{
				// 検索結果をロック対象リストに反映
				setList(retList, resultList);
			}
			
		}

		return retList;
	}
	
	/**
	 * 検索結果の値をリストに追加する。
	 * @param keyLst ロック対象リスト
	 * @param tgtLst 検索結果
	 */
	private void setList(ArrayList<String> keyLst, ArrayList<HashMap<String, String>> tgtLst)
	{
		
		// 設定済み請求契約番号保持用
		HashMap<String, String> hash = new HashMap<String, String>();
		
		for(int k = 0; k < keyLst.size(); k++)
		{
			// 現在のkey情報の詰め替え
			hash.put(keyLst.get(k), keyLst.get(k));
		}
		
		for(int i = 0; i < tgtLst.size(); i++)
		{
			String tgtSeikyKeiNo = tgtLst.get(i).get(KK1681ETMsg.SEIKY_KEI_NO);
			String tgtHgskSeikyKeiNo = tgtLst.get(i).get(KK1681ETMsg.HEIGO_SK_SEIKY_KEI_NO);
			String tgtBkskSeikyKeiNo = tgtLst.get(i).get(KK1681ETMsg.BUNKATSU_SK_SEIKY_KEI_NO);
			
			// 検索結果のkeyLst内の存在判定を行う
			// 請求契約番号
			if(tgtSeikyKeiNo != null && !"".equals(tgtSeikyKeiNo))
			{
				if(!hash.containsKey(tgtSeikyKeiNo))
				{
					// 一致する値が存在しない場合にkeyLstに追加する
					hash.put(tgtSeikyKeiNo, tgtSeikyKeiNo);
					keyLst.add(tgtSeikyKeiNo);
				}
			}
			
			// 併合先請求契約番号
			if(tgtHgskSeikyKeiNo != null && !"".equals(tgtHgskSeikyKeiNo))
			{
				if(!hash.containsKey(tgtHgskSeikyKeiNo))
				{
					hash.put(tgtHgskSeikyKeiNo, tgtHgskSeikyKeiNo);
					keyLst.add(tgtHgskSeikyKeiNo);
				}
			}
			
			// 分割先請求契約番号
			if(tgtBkskSeikyKeiNo != null && !"".equals(tgtBkskSeikyKeiNo))
			{
				if(!hash.containsKey(tgtBkskSeikyKeiNo))
				{
					hash.put(tgtBkskSeikyKeiNo, tgtBkskSeikyKeiNo);
					keyLst.add(tgtBkskSeikyKeiNo);
				}
			}		
		}
	}

	/**
	 * 対象請求契約番号の値をリストに追加する。
	 * @param seikyLst 請求契約番号リスト
	 * @param schLst 検索結果
	 */
	private void setSeiky(ArrayList<String> seikyLst, ArrayList<String> schLst)
	{
		
		// 設定済みSYSID保持用
		HashMap<String, String> hash = new HashMap<String, String>();
		
		for (int j = 0; j < seikyLst.size(); j++)
		{
			// 現在のkey情報の詰め替え
			hash.put(seikyLst.get(j), seikyLst.get(j));
		}
		
		for (int i = 0; i < schLst.size(); i++)
		{
			
			// 検索結果のseikyLst内の存在判定を行う
			if(schLst.get(i) != null && !"".equals(schLst.get(i)))
			{
				if(!hash.containsKey(schLst.get(i)))
				{
					// 一致する値が存在しない場合にseikyLstに追加する
					hash.put(schLst.get(i), schLst.get(i));
					seikyLst.add(schLst.get(i));
				}
			}
			
		}
	}

	/**
	 * 異動予約に対する請求契約番号の検索を行う。
	 * @param key 請求契約番号
	 * @param opeDate 運用日時
	 * @return 検索結果
	 */
	private ArrayList<HashMap<String, String>> searchSeikyKeiNo(String key, String opeDate)
	{
		
		// コネクション
		Connection con = null;
		// プリペアステートメント
		PreparedStatement pstmt = null;
		// リザルトセット
		ResultSet rsltQuery = null;
		// SQL文
		StringBuffer sbSql = new StringBuffer();
		
		sbSql.append(" SELECT ");
		sbSql.append("    NULL SEIKY_KEI_NO, ");
		sbSql.append("    KK1681.HEIGO_SK_SEIKY_KEI_NO, ");
		sbSql.append("    KK1681.BUNKATSU_SK_SEIKY_KEI_NO ");
		sbSql.append(" FROM ");
		sbSql.append("    KK_T_IDO_RSV KK1681 ");
		sbSql.append(" WHERE KK1681.SEIKY_KEI_NO = ? ");
		sbSql.append(" AND   KK1681.RSV_APLY_YMD > ?  ");
		sbSql.append(" UNION ");
		sbSql.append(" SELECT ");
		sbSql.append("    KK1681.SEIKY_KEI_NO, ");
		sbSql.append("    NULL HEIGO_SK_SEIKY_KEI_NO, ");
		sbSql.append("    NULL BUNKATSU_SK_SEIKY_KEI_NO ");
		sbSql.append(" FROM ");
		sbSql.append("    KK_T_IDO_RSV KK1681 ");
		sbSql.append(" WHERE KK1681.HEIGO_SK_SEIKY_KEI_NO = ? ");
		sbSql.append(" AND   KK1681.RSV_APLY_YMD > ?  ");
		sbSql.append(" UNION ");
		sbSql.append(" SELECT ");
		sbSql.append("    KK1681.SEIKY_KEI_NO, ");
		sbSql.append("    NULL HEIGO_SK_SEIKY_KEI_NO, ");
		sbSql.append("    NULL BUNKATSU_SK_SEIKY_KEI_NO ");
		sbSql.append(" FROM ");
		sbSql.append("    KK_T_IDO_RSV KK1681 ");
		sbSql.append(" WHERE KK1681.BUNKATSU_SK_SEIKY_KEI_NO = ? ");
		sbSql.append(" AND   KK1681.RSV_APLY_YMD > ?  ");

		try
		{
			// 対象テーブルのコネクション取得
			con = JSYejbConnection.getConnection(KK1681ETMsg.getTableName());

			// prepareStatementにSQL文をセット
			pstmt = con.prepareStatement(sbSql.toString());

			// ログ出力(SQL文の出力)
			JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), sbSql);

			// バインド変数設定
			CAANJDBCUtil.setParam(pstmt, 1, key);
			CAANJDBCUtil.setParam(pstmt, 2, opeDate);
			
			CAANJDBCUtil.setParam(pstmt, 3, key);
			CAANJDBCUtil.setParam(pstmt, 4, opeDate);
			
			CAANJDBCUtil.setParam(pstmt, 5, key);
			CAANJDBCUtil.setParam(pstmt, 6, opeDate);

			// ResultSetの取得
			rsltQuery = pstmt.executeQuery();

			// 格納マップの生成
			ArrayList<HashMap<String, String>> rsltList = new ArrayList<HashMap<String, String>>();
			
			// 取得結果をマップに格納
			while (rsltQuery.next())
			{
				// 格納マップの生成
				HashMap<String, String> mapRslt = new HashMap<String, String>();
				mapRslt.put(KK1681ETMsg.SEIKY_KEI_NO, rsltQuery.getString(1));
				mapRslt.put(KK1681ETMsg.HEIGO_SK_SEIKY_KEI_NO, rsltQuery.getString(2));
				mapRslt.put(KK1681ETMsg.BUNKATSU_SK_SEIKY_KEI_NO, rsltQuery.getString(3));

				rsltList.add(mapRslt);
			}

			// 取得した情報を返却
			return rsltList;

		}
		catch (SQLException e)
		{
			throw new CAANRuntimeException(e);
		}
		finally
		{
			// 資源の解放
			try
			{
				if (rsltQuery != null)
				{
					rsltQuery.close();
				}
				if (pstmt != null)
				{
					pstmt.close();
				}
				if (con != null)
				{
					CAANConnectionMgr.getInstance().close(con);
				}
			}
			catch (SQLException e1)
			{
				throw new CAANRuntimeException(e1);
			}
		}
		
	}
	
	/**
	 * <p>
	 * パラメータで渡された請求契約に該当する
	 * 請求契約テーブルで最後に更新されたレコードを取得する。
	 * </p>
	 * @param  primaryKey 請求契約番号
	 * @return rsltList   検索結果
	 */
	public ArrayList<HashMap<String, String>> getLastUpdRecordKK0491(String primaryKey)
	{
		// コネクション
		Connection con = null;
		// プリペアステートメント
		PreparedStatement pstmt = null;
		// リザルトセット
		ResultSet rsltQuery = null;
		// SQL文
		StringBuffer sbSql = new StringBuffer();

		sbSql.append(" SELECT ");
		sbSql.append("    KK0491.SEIKY_KEI_NO, ");
		sbSql.append("    KK0491.GENE_ADD_DTM, ");
		sbSql.append("    KK0491.UPD_DTM ");
		sbSql.append(" FROM ");
		sbSql.append("    KK_T_SEIKY_KEI KK0491 ");
		sbSql.append(" WHERE   KK0491.SEIKY_KEI_NO = ? ");
		sbSql.append(" AND     (KK0491.SEIKY_KEI_NO, KK0491.UPD_DTM) =  ");
		sbSql.append("          (SELECT KK0491_01.SEIKY_KEI_NO, MAX(KK0491_01.UPD_DTM) AS KK0491_MAX ");
		sbSql.append("           FROM   KK_T_SEIKY_KEI KK0491_01 ");
		sbSql.append("           WHERE  KK0491_01.SEIKY_KEI_NO = KK0491.SEIKY_KEI_NO ");
		sbSql.append("           GROUP BY KK0491_01.SEIKY_KEI_NO) ");
		
		try
		{
			// 対象テーブルのコネクション取得
			con = JSYejbConnection.getConnection(KK0491ETMsg.getTableName());

			// prepareStatementにSQL文をセット
			pstmt = con.prepareStatement(sbSql.toString());

			// ログ出力(SQL文の出力)
			JSYejbLog.println(JSYejbLog.DEBUG, this.getClass(), sbSql);

			// バインド変数設定
			CAANJDBCUtil.setParam(pstmt, 1, primaryKey);

			// ResultSetの取得
			rsltQuery = pstmt.executeQuery();

			// 格納マップの生成
			ArrayList<HashMap<String, String>> rsltList = new ArrayList<HashMap<String, String>>();
			
			// 取得結果をマップに格納
			while (rsltQuery.next())
			{
				// 格納マップの生成
				HashMap<String, String> mapRslt = new HashMap<String, String>();

				//請求契約の情報を返却
				mapRslt.put(KK0491ETMsg.SEIKY_KEI_NO, rsltQuery.getString(1));
				mapRslt.put(KK0491ETMsg.GENE_ADD_DTM, rsltQuery.getString(2));
				mapRslt.put(KK0491ETMsg.UPD_DTM, rsltQuery.getString(3));

				rsltList.add(mapRslt);
			}

			// 取得した情報を返却
			return rsltList;

		}
		catch (SQLException e)
		{
			throw new CAANRuntimeException(e);
		}
		finally
		{
			// 資源の解放
			try
			{
				if (rsltQuery != null)
				{
					rsltQuery.close();
				}
				if (pstmt != null)
				{
					pstmt.close();
				}
				if (con != null)
				{
					CAANConnectionMgr.getInstance().close(con);
				}
			}
			catch (SQLException e1)
			{
				throw new CAANRuntimeException(e1);
			}
		}
	}

	/**
	 * 請求契約のロック対象のＰＫをログに出力します。
	 * @param keyList ロック対象の請求契約番号
	 */
	private void outLogLockList(ArrayList<String> keyList)
	{
		for (int i = 0; i < keyList.size(); i++)
		{
			// ロック対象キーの出力
			JSYejbLog.println(JSYejbLog.DEBUG, getClass(), "JKKejbExclusiveProcKK0441.seiky_kei_no(Lock)=" + keyList.get(i));
		}
	}

}
