/*
 * Copyright (c) 2009 The openGion Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.opengion.hayabusa.mail;

import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;
import java.util.concurrent.ConcurrentMap;							// 6.4.3.3 (2016/03/04)
import java.util.concurrent.ConcurrentHashMap;						// 6.4.3.3 (2016/03/04)

import org.opengion.fukurou.db.DBUtil;
import org.opengion.fukurou.mail.MailTX;
import org.opengion.fukurou.system.LogWriter;
import org.opengion.fukurou.system.DateSet;							// 6.4.2.0 (2016/01/29)
import org.opengion.fukurou.util.StringUtil;
import org.opengion.hayabusa.common.HybsSystem;
import static org.opengion.fukurou.system.HybsConst.CR ;			// 6.1.0.0 (2014/12/26)
import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;	// 6.1.0.0 (2014/12/26) refactoring

/**
 * パッチによるメール送信の実装クラスです。
 * 送信デーモンはパラメータテーブル(GE30)を監視して、新規のデータが登録されたら、
 * そのデータをパラメータとしてメール合成処理メソッドに渡して合成を行って送信します。
 * 最後に、処理結果を受取って、パラメータテーブルの状況フラグを送信済/送信エラーに更新します。
 * エラーが発生した場合、エラーテーブルにエラーメッセージを書き込みます。
 *
 * @og.rev 5.9.26.0 (2017/11/02) 子クラスで利用する定数をprivateからprotectedに変更
 * @og.group メールモジュール
 *
 * @version  4.0
 * @author   Sen.Li
 * @since    JDK1.6
 */
public class MailManager_DB extends DefaultMailManager {
	// 5.9.26.0 (2017/11/02) 子クラスで利用定数を、privateからprotectedに変更
	// 5.2.0.0 (2010/09/01) Ver4互換モード対応
	private static final String H_TXT = HybsSystem.sysBool( "VER4_COMPATIBLE_MODE" ) ? "HEADER" : "H_TXT";
	private static final String F_TXT = HybsSystem.sysBool( "VER4_COMPATIBLE_MODE" ) ? "FOOTER" : "F_TXT";

	// 5.2.0.0 (2010/09/01) Ver4互換モード対応
	// 6.4.1.1 (2016/01/16) selGE30 → SEL_GE30 , insGE36 → INS_GE36 , updGE30 → UPD_GE30 refactoring
	protected static final String	SEL_GE30	= "SELECT  UNIQ,PTN_ID,FROM_ID,TO_ID,CC_ID,BCC_ID,"+H_TXT+","+F_TXT
										+",PARAM0,PARAM1,PARAM2,PARAM3,PARAM4,PARAM5,PARAM6,PARAM7,PARAM8,PARAM9"
										+",ATTACH1,ATTACH2,ATTACH3,ATTACH4,ATTACH5"
										+ " FROM GE30"
										+ " WHERE SYSTEM_ID =? AND FGJ='1'"
										+ " AND (SNDTIME IS NULL OR SNDTIME <= ? )";	// 5.9.18.0 (2017/03/02)
	protected static final String	INS_GE36	= "INSERT INTO GE36(PARA_KEY,ERRMSG,DYSET,USRSET,PGUPD,SYSTEM_ID,FGJ)"
										+ " VALUES(?,?,?,?,?,?,'1')";
	private static final String	UPD_GE30	= "UPDATE GE30 SET FGJ= ? WHERE UNIQ = ? ";
	protected static final String	SNED_OK	= "2";
	protected static final String	SNED_NG	= "8";
	protected static final int GE30_UNIQ		= 0 ;
	private static final int GE30_PTN_ID	= 1 ;
	private static final int GE30_FROM_ID	= 2 ;
	private static final int GE30_TO_ID		= 3 ;
	private static final int GE30_CC_ID		= 4 ;
	private static final int GE30_BCC_ID	= 5 ;
	private static final int GE30_H_TXT		= 6 ;		// 5.0.3.0 (2009/11/04) HEADER ⇒ H_TXT
	private static final int GE30_F_TXT		= 7 ;		// 5.0.3.0 (2009/11/04) FOOTER ⇒ F_TXT
	private static final int GE30_PARAM0	= 8 ;
	private static final int GE30_PARAM1	= 9 ;
	private static final int GE30_PARAM2	= 10 ;
	private static final int GE30_PARAM3	= 11 ;
	private static final int GE30_PARAM4	= 12 ;
	private static final int GE30_PARAM5	= 13 ;
	private static final int GE30_PARAM6	= 14 ;
	private static final int GE30_PARAM7	= 15 ;
	private static final int GE30_PARAM8	= 16 ;
	private static final int GE30_PARAM9	= 17 ;
	private static final int GE30_ATTACH1	= 18 ;
	private static final int GE30_ATTACH2	= 19 ;
	private static final int GE30_ATTACH3	= 20 ;
	private static final int GE30_ATTACH4	= 21 ;
	private static final int GE30_ATTACH5	= 22 ;
	protected static final int GE36_PARA_KEY	= 0 ;
	protected static final int GE36_ERRMSG	= 1 ;
	protected static final int GE36_DYSET		= 2 ;
	protected static final int GE36_USRSET	= 3 ;
	protected static final int GE36_PGUPD	= 4 ;
	protected static final int GE36_SYSTEM_ID	= 5 ;
	protected final List<String>	 errMsgList 	= new ArrayList<>();

	/**
	 * デフォルトコンストラクター
	 *
	 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
	 */
	public MailManager_DB() { super(); }		// これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。

	/**
	 * バッチより呼出のメインメソッドです。
	 * パラメータテーブル(GE30)を監視します。
	 * 新規のデータが登録されたら、メール文を合成して送信を行います。
	 * エラーが発生した場合、エラーテーブルにエラーメッセージを書き込みます。
	 *
	 * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策
	 * @og.rev 6.4.3.3 (2016/03/04) ConcurrentHashMap を受け取ることを明確にするため、I/FをConcurrentMapに変更します。
	 * @og.rev 5.9.18.0 (2017/03/02) SNDTIME対応
	 *
	 * @param systemId システムID
	 */
	public void sendDBMail( final String systemId ){
		// パラメータテーブルよりバッチでセットしたデータを取得します。
		final String[][] ge30datas = DBUtil.dbExecute( SEL_GE30, new String[]{ systemId, DateSet.getDate( "yyyyMMddHHmmss" ) }, APP_INFO, DBID );	// 5.9.18.0 (2017/03/02)

		final int ge30Len = ge30datas.length;
		for( int i=0; i<ge30Len; i++ ) {
			String fgj = SNED_OK;
			try {
				final ConcurrentMap<String, String> initParam = makeParamMap( systemId, ge30datas[i] );
				create( initParam );
				send();								// 合成されたメール文書、宛先で送信処理を行います。
				errMsgList.addAll( getErrList() );
			}
			catch( final RuntimeException rex ) {
				fgj = SNED_NG;
				errMsgList.add( "メール送信失敗しました。パラメータキー：" + ge30datas[i][GE30_UNIQ] + " " + rex.getMessage() );
			}
			finally {
				commitParamTable( ge30datas[i][GE30_UNIQ], fgj );
				if( ! errMsgList.isEmpty() ) {
					writeErrorTable( ge30datas[i][GE30_UNIQ], systemId, errMsgList );
					errMsgList.clear();
				}
			}
		}
	}

	/**
	 * パラメータテーブルに登録したデータをパラメータマップにセットします。
	 *
	 * @og.rev 6.4.2.0 (2016/01/29) DateSet.getDate( String ) を利用するように修正します。
	 * @og.rev 6.4.3.3 (2016/03/04) ConcurrentHashMap を受け取ることを明確にするため、I/FをConcurrentMapに変更します。
	 * @og.rev 5.9.26.0 (2017/11/02) 子クラスでの利用対応。privateをprotectedに変更。
	 *
	 * @param	systemId	システムID
	 * @param	ge30Data	パラメータテーブルのデータ配列
	 *
	 * @return	データをセットしたマップ
	 */
	protected ConcurrentMap<String, String> makeParamMap( final String systemId, final String[] ge30Data ){				// 6.4.3.3 (2016/03/04) ここは可変長はまずいでしょ。
		ConcurrentMap<String,String> paramMap = null;
		if( ge30Data != null && ge30Data.length > 0 ) {		// 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。
			paramMap = new ConcurrentHashMap<>();
			paramMap.put( "SYSTEM_ID"	, systemId    );
			paramMap.put( "PARAKEY"		, ge30Data[GE30_UNIQ]    );
			paramMap.put( "PTN_ID"		, ge30Data[GE30_PTN_ID]  );
			paramMap.put( "FROM"		, ge30Data[GE30_FROM_ID] );
			paramMap.put( "TO"			, ge30Data[GE30_TO_ID]   );
			paramMap.put( "CC"			, ge30Data[GE30_CC_ID]   );
			paramMap.put( "BCC"			, ge30Data[GE30_BCC_ID]  );
			paramMap.put( "H_TXT"		, ge30Data[GE30_H_TXT]   );			// 5.0.3.0 (2009/11/04) HEADER ⇒ H_TXT
			paramMap.put( "F_TXT"		, ge30Data[GE30_F_TXT]   );			// 5.0.3.0 (2009/11/04) FOOTER ⇒ F_TXT
			paramMap.put( "PARAM0"		, ge30Data[GE30_PARAM0]  );
			paramMap.put( "PARAM1" 		, ge30Data[GE30_PARAM1]  );
			paramMap.put( "PARAM2"		, ge30Data[GE30_PARAM2]  );
			paramMap.put( "PARAM3"		, ge30Data[GE30_PARAM3]  );
			paramMap.put( "PARAM4"		, ge30Data[GE30_PARAM4]  );
			paramMap.put( "PARAM5"		, ge30Data[GE30_PARAM5]  );
			paramMap.put( "PARAM6"		, ge30Data[GE30_PARAM6]  );
			paramMap.put( "PARAM7"		, ge30Data[GE30_PARAM7]  );
			paramMap.put( "PARAM8"		, ge30Data[GE30_PARAM8]  );
			paramMap.put( "PARAM9"		, ge30Data[GE30_PARAM9]  );
			paramMap.put( "ATTACH1"		, ge30Data[GE30_ATTACH1] );
			paramMap.put( "ATTACH2"		, ge30Data[GE30_ATTACH2] );
			paramMap.put( "ATTACH3"		, ge30Data[GE30_ATTACH3] );
			paramMap.put( "ATTACH4"		, ge30Data[GE30_ATTACH4] );
			paramMap.put( "ATTACH5"		, ge30Data[GE30_ATTACH5] );
			paramMap.put( "DATE"		, DateSet.getDate("yyyy/MM/dd") );		// 6.4.2.0 (2016/01/29)
			paramMap.put( "TIME"		, DateSet.getDate("HH:mm:ss") );		// 6.4.2.0 (2016/01/29)
			paramMap.put( "LOGIN_USERID", "DAEMON" );
			paramMap.put( "LOGIN_USERNAME", "DAEMON" );
			paramMap.put( "PGID", "DAEMON" );
		}
		return paramMap;
	}

	/**
	 * 送信後、パラメータテーブルの状況フラグを更新します。
	 * 送信エラーなしの場合はフラグを’送信済(2)’、エラーの場合’送信エラー(8)’に更新します。
	 *
	 * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策
	 * @og.rev 5.9.26.0 (2017/11/02) 子クラスでの利用対応。privateをprotectedに変更。
	 *
	 * @param	uniq	ユニークキー
	 * @param	fgj		状況フラグ[2:送信済/8:エラー]
	 */
	protected void commitParamTable( final String uniq, final String fgj ){
		final String[] updGE30Args = { fgj, uniq };
		DBUtil.dbExecute( UPD_GE30, updGE30Args, APP_INFO, DBID );		// 5.5.5.1 (2012/08/07)
	}

	/**
	 * エラーテーブルにエラーメッセージを登録します。
	 *
	 * @og.rev 4.4.0.1 (2009/08/08) メール送信追加
	 * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策
	 * @og.rev 6.4.2.0 (2016/01/29) DateSet.getDate( String ) を利用するように修正します。
	 *
	 * @param	paraKey		パラメータキー(GE36.PARA_KEY)
	 * @param	systemId	システムID
	 * @param	emList		エラーメッセージリスト
	 *
	 */
	private void writeErrorTable( final String paraKey, final String systemId, final List<String> emList ){
		String[] insGE36Args = new String[6];
		insGE36Args[GE36_PARA_KEY]	= paraKey;
		insGE36Args[GE36_DYSET] 	= DateSet.getDate( "yyyyMMddHHmmss" );	// 6.4.2.0 (2016/01/29)
		insGE36Args[GE36_USRSET] 	= "DAEMON";
		insGE36Args[GE36_PGUPD] 	= "DAEMON";
		insGE36Args[GE36_SYSTEM_ID] = systemId;
		for( int i=0; i< emList.size(); i++ ){
			insGE36Args[GE36_ERRMSG] = trim( emList.get( i ), 4000);
			DBUtil.dbExecute( INS_GE36, insGE36Args, APP_INFO, DBID );		// 5.5.5.1 (2012/08/07)
		}

		sendMail( paraKey, systemId, emList ); // 4.4.0.1 (2009/08/08)
	}

	/**
	 * エラー情報のメール送信を行います。
	 * エラーメールは、システムパラメータ の COMMON_MAIL_SERVER(メールサーバー)と
	 * ERROR_MAIL_FROM_USER(エラーメール発信元)と、ERROR_MAIL_TO_USERS(エラーメール受信者)
	 * がすべて設定されている場合に、送信されます。
	 *
	 * @og.rev 4.4.0.1 (2009/08/08) 追加
	 * @og.rev 5.4.3.2 (2012/01/06) 認証対応
	 * @og.rev 6.0.3.0 (2014/11/13) Ver6用キーワード変更
	 * @og.rev 6.3.8.0 (2015/09/11) SSL接続するかどうかを指定するパラメータを追加します。
	 * @og.rev 6.3.8.0 (2015/09/11) SSL接続するかどうかを指定するパラメータを追加します。
	 * @og.rev 5.9.29.2 (2018/02/16) STARTTLS対応(ｷｰﾜｰﾄﾞをVer5 にあわせます)
	 *
	 * @param	paraKey		メールキー
	 * @param	systemId	システムID
	 * @param	emList		エラーメッセージリスト
	 */
	private void sendMail( final String paraKey, final String systemId, final List<String> emList ) {

		final String	host		= HybsSystem.sys( "COMMON_MAIL_SERVER" );
		final String	from		= HybsSystem.sys( "ERROR_MAIL_FROM_USER" );
		final String	charset		= HybsSystem.sys( "MAIL_DEFAULT_CHARSET" );
		final String	smtpPort	= HybsSystem.sys( "SMTP_PORT" );				// 5.4.3.2 (2012/01/06)
		final String	authType	= HybsSystem.sys( "MAIL_SEND_AUTH" );			// 6.0.3.0 (2014/11/13) Ver6用キーワード変更
		final String	authPort	= HybsSystem.sys( "MAIL_SEND_AUTH_PORT" );		// 5.8.1.1 (2014/11/14)
		final String	authUser	= HybsSystem.sys( "MAIL_SEND_AUTH_USER" );		// 5.4.3.2 (2012/01/06)
		final String	authPass	= HybsSystem.sys( "MAIL_SEND_AUTH_PASSWORD" );	// 5.4.3.2 (2012/01/06)
		final boolean	useSSL		= HybsSystem.sysBool( "MAIL_SEND_USE_SSL" );	// 6.3.8.0 (2015/09/11)
		final boolean	useTLS		= HybsSystem.sysBool( "MAIL_SEND_USE_STARTTLS" );	 // 5.9.29.2(2018/02/16)

		final String[] to = StringUtil.csv2Array( HybsSystem.sys( "ERROR_MAIL_TO_USERS" ) );
		if( host != null && from != null && to.length > 0 ) {
			final String subject = "SYSTEM_ID=[" + systemId + "] , PARA_KEY=[" + paraKey + "] , "
						   + "DMN_HOST=[" + HybsSystem.HOST_NAME + "]" ;
			final StringBuilder inErrMsg = new StringBuilder( BUFFER_MIDDLE )
				.append( emList.size() ).append( "件のエラーがありました。" )		// 6.0.2.5 (2014/10/31) char を append する。
				.append( CR );
			for( int i=0; i< emList.size(); i++ ){
				inErrMsg.append( i+1 )
					.append( "-----" )
					.append( CR )
					.append( emList.get( i ) )
					.append( CR );
			}
			try {
				//MailTX tx = new MailTX( host );
//				final MailTX tx = new MailTX( host, charset, smtpPort, authType, authPort, authUser, authPass, useSSL );	// 6.3.8.0 (2015/09/11)
				final MailTX tx = new MailTX( host, charset, smtpPort, authType, authPort, authUser, authPass, useTLS, useSSL );	// 5.9.29.2
				tx.setFrom( from );
				tx.setTo( to );
				tx.setSubject( "メールモジュール送信エラー：" + subject );
				tx.setMessage( inErrMsg.toString() );
				tx.sendmail();
			}
			catch( final Throwable ex ) {
				final String errMsg = "エラー時メール送信に失敗しました。" + CR
							+ " SUBJECT:" + subject					+ CR
							+ " HOST:" + host						+ CR
							+ " FROM:" + from						+ CR
							+ " TO:"   + Arrays.toString( to )		+ CR
							+ ex.getMessage();		// 5.1.8.0 (2010/07/01) errMsg 修正
				LogWriter.log( errMsg );
				LogWriter.log( ex );
			}
		}
	}
}
