package com.ym.mec.auth.core.filter;

import java.io.IOException;

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

import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;

import com.ym.mec.auth.api.dto.SysUserInfo;
import com.ym.mec.auth.service.SysUserDeviceService;
import com.ym.mec.auth.service.SysUserService;
import com.ym.mec.common.security.SecurityConstants;

public class UsernameAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

	private SysUserService sysUserService;
	
	private SysUserDeviceService sysUserDeviceService;

	// ~ Static fields/initializers
	// =====================================================================================

	private String usernameParameter = "username";
	private String passwordParameter = "password";
	private String clientIdParameter = "clientId";
	private String deviceNumParameter = "deviceNum";
	private boolean postOnly = true;

	// ~ Constructors
	// ===================================================================================================

	public UsernameAuthenticationFilter() {
		super(new AntPathRequestMatcher("/usernameLogin", "POST"));
	}

	// ~ Methods
	// ========================================================================================================

	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
		if (postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
		}

		String username = obtainUsername(request);
		String password = obtainPassword(request);
		if (username == null) {
			username = "";
		}

		if (password == null) {
			password = "";
		}

		username = username.trim();
		password = password.trim();

		SysUserInfo userInfo = sysUserService.queryUserInfoByUsername(username);

		String clientId = request.getParameter(clientIdParameter).toUpperCase();

		if (userInfo == null) {
			throw new UsernameNotFoundException("404.9");
		}
	
//		if (userInfo.getSysUser().getUserType() != SysUserType.SYSTEM && !StringUtils.equalsIgnoreCase(clientId, userInfo.getSysUser().getUserType().getCode())) {
//			throw new LockedException("登录失败");
//		}
		if("EDUCATION".equals(clientId)){
			clientId = "SYSTEM";
		}
		if (!userInfo.getSysUser().getUserType().contains(clientId)) {
			throw new LockedException("用户不存在,请联系教务老师");
		}

		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(SecurityConstants.USERNAME_PRINCIPAL_PREFIX + username,
				password);

		// Allow subclasses to set the "details" property
		setDetails(request, authRequest);
		
		Authentication authentication = this.getAuthenticationManager().authenticate(authRequest);
		
		String deviceNum = request.getParameter(deviceNumParameter);
		if (StringUtils.isNotBlank(deviceNum) && !StringUtils.equals("STUDENT", clientId)) {
			// 检查设备
			try {
				sysUserDeviceService.bindDevice(userInfo.getSysUser().getId(), deviceNum);
			} catch (Exception e) {
				throw new BadCredentialsException("当前设备已绑定账号，请更换设备");
			}
		}

		return authentication;
	}

	@Override
	protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult)
			throws IOException, ServletException {
		super.successfulAuthentication(request, response, chain, authResult);
		// chain.doFilter(request, response);
	}

	/**
	 * Enables subclasses to override the composition of the password, such as by
	 * including additional values and a separator.
	 * <p>
	 * This might be used for example if a postcode/zipcode was required in addition to
	 * the password. A delimiter such as a pipe (|) should be used to separate the
	 * password and extended value(s). The <code>AuthenticationDao</code> will need to
	 * generate the expected password in a corresponding manner.
	 * </p>
	 *
	 * @param request so that request attributes can be retrieved
	 *
	 * @return the password that will be presented in the <code>Authentication</code>
	 * request token to the <code>AuthenticationManager</code>
	 */
	protected String obtainPassword(HttpServletRequest request) {
		return request.getParameter(passwordParameter);
	}

	/**
	 * Enables subclasses to override the composition of the username, such as by
	 * including additional values and a separator.
	 *
	 * @param request so that request attributes can be retrieved
	 *
	 * @return the username that will be presented in the <code>Authentication</code>
	 * request token to the <code>AuthenticationManager</code>
	 */
	protected String obtainUsername(HttpServletRequest request) {
		return request.getParameter(usernameParameter);
	}

	/**
	 * Provided so that subclasses may configure what is put into the authentication
	 * request's details property.
	 *
	 * @param request that an authentication request is being created for
	 * @param authRequest the authentication request object that should have its details
	 * set
	 */
	protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
		authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
	}

	/**
	 * Sets the parameter name which will be used to obtain the username from the login
	 * request.
	 *
	 * @param usernameParameter the parameter name. Defaults to "username".
	 */
	public void setUsernameParameter(String usernameParameter) {
		Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
		this.usernameParameter = usernameParameter;
	}

	/**
	 * Sets the parameter name which will be used to obtain the password from the login
	 * request..
	 *
	 * @param passwordParameter the parameter name. Defaults to "password".
	 */
	public void setPasswordParameter(String passwordParameter) {
		Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
		this.passwordParameter = passwordParameter;
	}

	/**
	 * Defines whether only HTTP POST requests will be allowed by this filter. If set to
	 * true, and an authentication request is received which is not a POST request, an
	 * exception will be raised immediately and authentication will not be attempted. The
	 * <tt>unsuccessfulAuthentication()</tt> method will be called as if handling a failed
	 * authentication.
	 * <p>
	 * Defaults to <tt>true</tt> but may be overridden by subclasses.
	 */
	public void setPostOnly(boolean postOnly) {
		this.postOnly = postOnly;
	}

	public final String getUsernameParameter() {
		return usernameParameter;
	}

	public final String getPasswordParameter() {
		return passwordParameter;
	}

	public void setSysUserService(SysUserService sysUserService) {
		this.sysUserService = sysUserService;
	}

	public void setSysUserDeviceService(SysUserDeviceService sysUserDeviceService) {
		this.sysUserDeviceService = sysUserDeviceService;
	}

}
