package jp.co.argo21.nautica.workflow.sample.control;

import java.io.IOException;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import jp.co.argo21.nautica.workflow.sample.model.AppTransaction;
import jp.co.argo21.nautica.workflow.sample.model.Application;
import jp.co.argo21.nautica.workflow.sample.model.dao.AppTransactionDAO;
import jp.co.argo21.nautica.workflow.sample.model.dao.ApplicationDAO;
import jp.co.argo21.nautica.workflow.sample.model.dao.DAOFactory;
import jp.co.argo21.nautica.workflow.sample.util.Constants;
import jp.co.argo21.nautica.workflow.sample.util.HtmlUtil;
import jp.co.argo21.nautica.workflow.sample.util.ServiceAccessor;
import jp.co.argo21.nautica.workflow.sample.util.SingletonServices;
import jp.co.argo21.nautica.workflow.sample.util.StateConstants;
import jp.co.argo21.nautica.workflow.sample.util.WebServiceUtil;
import jp.co.argo21.nautica.workflow.soap.InvalidSessionException;
import jp.co.argo21.nautica.workflow.soap.InvalidWorkItemException;
import jp.co.argo21.nautica.workflow.soap.RoleManager;
import jp.co.argo21.nautica.workflow.soap.WorkItemHandler;
import jp.co.argo21.nautica.workflow.soap.message.Attribute;
import jp.co.argo21.nautica.workflow.soap.message.Filter;
import jp.co.argo21.nautica.workflow.soap.message.Role;
import jp.co.argo21.nautica.workflow.soap.message.User;
import jp.co.argo21.nautica.workflow.soap.message.WorkItem;

/**
 * 照査する。
 * 
 * @author stokin
 *
 */
public class Check extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {

	/**
	 * doPostに処理を委譲する。
	 * 
	 * @param req リクエスト
	 * @param res レスポンス
	 * @throws ServletException 発生しない
	 * @throws IOException forwardに失敗した場合
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doPost(request, response);
	}  	

	/**
	 * 照査する。
	 * 
	 * @param req リクエスト
	 * @param res レスポンス
	 * @throws ServletException 発生しない
	 * @throws IOException forwardに失敗した場合
	 */
	protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		HttpSession session = req.getSession(true);
		req.setCharacterEncoding("UTF-8");
		User user = (User)session.getAttribute("user");
		String sessionID = (String)session.getAttribute("sessionID");
		String appID = (String)req.getParameter("appID");
		String pressedButton = (String)req.getParameter("pressedButton");

		String preComment = (String)req.getParameter("comment");
		// 入力内容をサニタイズする
		String comment = HtmlUtil.sanitize(preComment);

		ServletContext context = getServletContext();
		RequestDispatcher dispatcher = null;
		try{
			// 押下されたボタンに応じて処理を振り分ける
			if (pressedButton.equals("承認")){
				approve(sessionID, user, appID, comment);
			} else if (pressedButton.equals("差し戻し")){
				retry(sessionID, user, appID, comment);
			} else if (pressedButton.equals("否認")){
				deny(sessionID, user, appID, comment);
			} else if (pressedButton.equals("確認")){
				confirm(sessionID, user, appID);
			}

			req.setAttribute("appID", appID);
			req.setAttribute("pressedButton", pressedButton);
			context = getServletContext();
			dispatcher = context.getRequestDispatcher("/Complete.jsp");
		} catch (Exception e){
			// 致命的エラー
			session.invalidate();
			req.setAttribute("errMsg", e.toString());
			req.setAttribute("err", e.getStackTrace());
			dispatcher = context.getRequestDispatcher("/FatalError.jsp");
		}		
		dispatcher.forward(req, res);
	}

	/**
	 * 承認する。
	 * 
	 * @param sessionID セッションID
	 * @param user ユーザ
	 * @param appID 申請書番号
	 * @param comment コメント
	 * @throws Exception 承認処理が失敗した場合
	 */
	private void approve(String sessionID, User user, String appID, String comment) throws Exception{
		//申請トランザクションテーブルの[状態]が"待ち"になっている、最小の[承認ブロック番号]を取得する
		AppTransactionDAO appTransDao = DAOFactory.getAppTransactionDAO();
		AppTransaction[] appTranses = appTransDao.getAppTransactionsByAppID(appID);
		AppTransaction appTrans = null;
		AppTransaction nextAppTrans = null;
		for (int i = 0; i < appTranses.length; i++) {
			String status = appTranses[i].getAppStatus();
			if ((StateConstants.STATUS_TRANS_WAIT).equals(status)) {
				appTrans = appTranses[i];
				if ( i < appTranses.length-1) {
					nextAppTrans = appTranses[i+1];
				}
				break;
			}
		}

		// 取得した申請トランザクションテーブルの
		// [状態][申請者/承認者ユーザID][コメント][対象日]を更新
		if (appTrans != null) {
			updateAppTransaction(appTrans, StateConstants.STATUS_TRANS_APPROVE, user, comment);
		}

		// 申請トランザクションテーブルの更新した次のレコードの
		// [状態]を"WAIT"に更新
		ApplicationDAO appDao = DAOFactory.getApplicationDAO();
		Application app = appDao.getApplicationByID(appID);
		int approveAccount = app.getKind().getApproveAccount();
		if (nextAppTrans != null && nextAppTrans.getApproveAccountID() < approveAccount) {
			nextAppTrans.setAppStatus(StateConstants.STATUS_TRANS_WAIT);
			appTransDao.updateStatus(nextAppTrans);
		}

		// 作業項目のプロセス変数[承認結果]を"ACK"に設定
		WorkItemHandler wfWorkitemHandler = ServiceAccessor.getInstance().getWorkItemHandler();
		List<Filter> appIDFilter = WebServiceUtil.createEqualAttributeFilter(Constants.APPLICATION_NO, appID);
		List<WorkItem> workItems = wfWorkitemHandler.getWorkItems(sessionID, appIDFilter);
		// 作業項目は1つだけ取得できるため、0番目を決め打ち
		String wid = workItems.get(0).getID();
		WebServiceUtil.assignWorkItemAttribute(sessionID, wid, Constants.APPROVAL_RESULT, Constants.ACK);

		// 最終承認者の場合、プロセス変数[状態]を"CONFIRM_WAIT"に更新
		int approveCount = app.getKind().getApproveAccount();
		if (approveCount == appTrans.getApproveAccountID()+1) {
			WebServiceUtil.assignWorkItemAttribute(sessionID, wid, Constants.STATE, StateConstants.CONFIRM_WAIT);
		}

		// 作業項目の完了
		wfWorkitemHandler.completeWorkItem(sessionID,wid);
	}

	/**
	 * 差し戻す。
	 * 
	 * @param sessionID セッションID
	 * @param user ユーザ
	 * @param appID 申請書番号
	 * @param comment コメント
	 * @throws Exception 差し戻し処理に失敗した場合
	 */
	private void retry(String sessionID, User user, String appID, String comment) throws Exception{
		// 申請トランザクションテーブルの[状態]が"待ち"になっている、最小の[承認ブロック番号]を取得する
		AppTransactionDAO appTransDao = DAOFactory.getAppTransactionDAO();
		// 取得した申請トランザクションテーブルの
		// [状態][申請者/承認者ユーザID][コメント][対象日]を更新
		AppTransaction appTrans = getSmallestAppTrans(appID);
		if (appTrans != null) {
			updateAppTransaction(appTrans, StateConstants.STATUS_TRANS_RETRY, user, comment);

			// 申請者の[状態]を”待ち”に変更
			AppTransaction applyTrans = appTransDao.getAppTransactionByAppIDBlockID(appID, 0);
			applyTrans.setAppStatus(StateConstants.STATUS_TRANS_WAIT);
			appTransDao.updateAppTransaction(applyTrans);
		}

		// 作業項目のプロセス変数[承認結果]を"RETRY"に設定
		WorkItemHandler wiHandlerler = ServiceAccessor.getInstance().getWorkItemHandler();
		List<Filter> appIDFilter = WebServiceUtil.createEqualAttributeFilter(Constants.APPLICATION_NO, appID);
		List<WorkItem> workItems = wiHandlerler.getWorkItems(sessionID, appIDFilter);
		// 作業項目は1つだけ取得できるため、0番目を決め打ち
		String wid = workItems.get(0).getID();
		WebServiceUtil.assignWorkItemAttribute(sessionID, wid, Constants.APPROVAL_RESULT, Constants.RETRY);

		// 作業項目のプロセス変数[状態]に"APPLY_WAIT"を設定
		WebServiceUtil.assignWorkItemAttribute(sessionID, wid, Constants.STATE, StateConstants.APPLY_WAIT);
		
		// 作業項目の完了
		wiHandlerler.completeWorkItem(sessionID,wid);
	}

	/**
	 * 否認する。
	 * 
	 * @param sessionID セッションID
	 * @param user ユーザ
	 * @param appID 申請書番号
	 * @param comment コメント
	 * @throws Exception 否認処理に失敗した場合
	 */
	private void deny(String sessionID, User user, String appID, String comment) throws Exception{
		AppTransactionDAO appTransDao = DAOFactory.getAppTransactionDAO();
		// 取得した申請トランザクションテーブルの
		// [状態][申請者/承認者ユーザID][コメント][対象日]を更新
		AppTransaction appTrans =  getSmallestAppTrans(appID);
		if (appTrans != null) {
			updateAppTransaction(appTrans, StateConstants.STATUS_TRANS_DENIAL, user, comment);
		}

		// 作業項目のプロセス変数[承認結果]を"NACK"に設定
		WorkItemHandler wfWorkitemHandler = ServiceAccessor.getInstance().getWorkItemHandler();
		List<Filter> appIDFilter = WebServiceUtil.createEqualAttributeFilter(Constants.APPLICATION_NO, appID);
		List<WorkItem> workItems = wfWorkitemHandler.getWorkItems(sessionID, appIDFilter);
		// 作業項目は1つだけ取得できるため、0番目を決め打ち
		String wid = workItems.get(0).getID();

		WebServiceUtil.assignWorkItemAttribute(sessionID, wid, Constants.APPROVAL_RESULT, Constants.NACK);

		// 作業項目の完了
		wfWorkitemHandler.completeWorkItem(sessionID,wid);
	}

	/**
	 * 同報を確認する。
	 * 
	 * @param sessionID セッションID
	 * @param user ユーザ
	 * @param appID 申請書番号
	 * @throws Exception 同報確認処理に失敗した場合
	 */
	private void confirm(String sessionID, User user, String appID) throws Exception{

		WorkItemHandler wiHandler = ServiceAccessor.getInstance().getWorkItemHandler();
		List<Filter> appIDFilter = WebServiceUtil.createEqualAttributeFilter(Constants.APPLICATION_NO, appID);
		List<WorkItem> workItems = wiHandler.getWorkItems(sessionID, appIDFilter);
		// 作業項目は1つだけ取得できるため、0番目を決め打ち
		String wid = workItems.get(0).getID();

		// ログインユーザのロールを見て、プロセス変数を変更する
		RoleManager roleManager = ServiceAccessor.getInstance().getRoleManager();
		List<Role> attachedRoles = roleManager.getAttachedRoles2(user);
		Attribute attr = new Attribute();
		attr.setType(Constants.TYPE_BOOLEAN); 
		for (Role role : attachedRoles){
			if (role.getId().equals("JINJI_KACHO_ROLE")) {
				attr.setName("NOTIFICATION_JINJI_KACHO");
			} else if (role.getId().equals("JINJI_BUCHO_ROLE")) {
				attr.setName("NOTIFICATION_JINJI_BUCHO");
			}        
		}
		attr.setValue("true");
		wiHandler.assignWorkItemAttribute(sessionID, wid, attr);        

		// 作業項目の完了
		wiHandler.completeWorkItem(sessionID, wid);

		// 申請トランザクションテーブルの[状態]を変更
		AppTransactionDAO appTransDao = DAOFactory.getAppTransactionDAO();
		AppTransaction appTrans = appTransDao.getAppTransactionByAppIDUserID(appID, user.getId());
		appTrans.setAppStatus(StateConstants.STATUS_TRANS_CONFIRMED);
		appTransDao.updateStatus(appTrans);
	}

	/**
	 * 変更するべき申請トランザクションテーブルの１レコードを取得する。
	 * 
	 * @param appID 申請書番号
	 * @return　申請トランザクションテーブルの１レコード
	 * @throws Exception　申請トランザクションテーブルのレコードの取得に失敗した場合
	 */
	private AppTransaction getSmallestAppTrans(String appID) throws Exception{
		// 申請トランザクションテーブルの[状態]が"待ち"になっている、最小の[承認ブロック番号]を取得する
		AppTransactionDAO appTransDao = DAOFactory.getAppTransactionDAO();
		AppTransaction[] appTranses = appTransDao.getAppTransactionsByAppID(appID);
		AppTransaction appTrans = null;
		for (int i = 0; i < appTranses.length; i++) {
			String status = appTranses[i].getAppStatus();
			if ((StateConstants.STATUS_TRANS_WAIT).equals(status)) {
				appTrans = appTranses[i];
				break;
			}
		}
		return appTrans;
	}
	
	/**
	 * 申請トランザクションテーブルを更新する。
	 * 
	 * @param appTrans　申請トランザクションテーブルのレコード
	 * @param state　結果
	 * @param user　ユーザ
	 * @param comment　コメント
	 * @throws SQLException　申請トランザクションテーブルの更新に失敗した場合。
	 */
	private void updateAppTransaction(AppTransaction appTrans, String state, User user, String comment) throws SQLException{
		AppTransactionDAO appTransDao = DAOFactory.getAppTransactionDAO();
		appTrans.setAppStatus(state);
		appTrans.setAppUserID(user.getId());
		appTrans.setComment(comment);
		appTrans.setDate(new Timestamp(Calendar.getInstance().getTimeInMillis()));
		appTransDao.updateAppTransaction(appTrans);
	}
}