/*******************************************************************************
 * Copyright 2Fi Business Solutions Ltd.
 *
 * This code is copyrighted. Under no circumstances should any party, people,
 * or organization should redistribute any portions of this code in any form,
 * either verbatim or through electronic media, to any third parties, unless
 * under explicit written permission by 2Fi Business Solutions Ltd.
 ******************************************************************************/
package com.ffii.core.security.web.authentication;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;

import com.ffii.core.utils.BooleanUtils;
import com.ffii.core.utils.JsonUtils;
import com.ffii.core.utils.MapUtils;
import com.ffii.core.utils.Params;
import com.ffii.core.utils.StringUtils;
import com.ffii.core.utils.web.ServletRequestUtils;
import com.ffii.core.utils.web.ServletResponseUtils;
import com.ffii.core.web.view.AbstractView;
import com.ffii.core.security.service.LoginLogService;
import com.ffii.tbms.user.service.UserService;

/**
 * <tt>AuthenticationFailureHandler</tt> implementation which return a failure JSON String upon failed authentication.
 *
 * @author Patrick
 */
public class JsonAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

	/** Continuous fail 5 time, lock user */
	private final int TIMES = 5;

	protected final Log logger = LogFactory.getLog(getClass());

	@Autowired
	private UserService userService;

	@Autowired
	private LoginLogService loginLogService;

	/**
	 * Returns the failure JSON String to client.
	 */
	@Override
	public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)
			throws IOException, ServletException {

				logger.info("Authentication Failure: " + exception.getMessage() + " [" + request.getRequestURL() + "]");
		logger.info("Authentication Failure: " + exception.getMessage() + " [" + request.getRemoteAddr() + "]");

		String username = StringUtils.left(ServletRequestUtils.getStringParameter(request, "username"), 32);

		ServletResponseUtils.disableCaching(response);
		response.setContentType(AbstractView.CONTENT_TYPE_JSON);
		response.setCharacterEncoding("UTF-8");
		response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // set HTTP status to 401

		boolean locked = false;
		// log failed login
		if (username != null) {
			loginLogService.createLoginLog(username, request.getRemoteAddr(), false);

			// when failed 5 times, lock account
			List<Map<String, Object>> logs = loginLogService.listLastLog(username, TIMES);
			if (logs.size() >= TIMES) {
				boolean needLock = true;
				for (Map<String, Object> log : logs) {
					if (BooleanUtils.isTrue(log.get("success"))) {
						needLock = false;
						break;
					}
				}
				if (needLock) {
					locked = true;
					userService.lockUser(username, true);
				}
			}

		}
		Map<String, Object> result = MapUtils.toHashMap(
				Params.SUCCESS, Boolean.FALSE,
				Params.MSG,
				locked ? "Account locked (" + TIMES + " Times Failure), please contact your IT administrator." : "Invalid Username or Password.");

		ServletResponseUtils.writeStringToStream(response, AbstractView.CHARSET_UTF8, JsonUtils.toJsonString(result));
	}

}
