package com.uas.platform.b2b.filter;

import com.uas.platform.b2b.dao.ResourceItemDao;
import com.uas.platform.b2b.model.*;
import com.uas.platform.b2b.service.SigninLogService;
import com.uas.platform.b2b.service.UserService;
import com.uas.platform.b2b.support.SecurityConstant;
import com.uas.platform.b2b.support.SystemSession;
import com.uas.platform.core.model.Constant;
import com.uas.platform.core.util.AgentUtils;
import com.uas.platform.core.util.encry.Md5Utils;
import com.uas.platform.core.util.serializer.FlexJsonUtils;
import com.uas.sso.SSOHelper;
import com.uas.sso.SSOToken;
import com.uas.sso.entity.UserAccount;
import com.uas.sso.web.spring.AbstractSSOInterceptor;
import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.mobile.device.Device;
import org.springframework.mobile.device.DeviceResolver;
import org.springframework.mobile.device.LiteDeviceResolver;
import org.springframework.mobile.device.site.SitePreference;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

@SuppressWarnings("deprecation")
public class SSOInterceptor extends AbstractSSOInterceptor {

	private static final Logger logger = Logger.getLogger(SSOInterceptor.class);

	@Autowired
	private UserService userService;
	@Autowired
	private ResourceItemDao resourceItemDao;
	@Autowired
	private SigninLogService signinLogService;

	private final DeviceResolver deviceResolver = new LiteDeviceResolver();

	private HashMap<String, Collection<ConfigAttribute>> resourceMap;
	private HashMap<Long, Collection<GrantedAuthority>> authorities;

	private User getUserByToken(SSOToken token) {
		User authedUser = null;
		if (token.getData() != null) {
			UserAccount tokenUser = FlexJsonUtils.fromJson(token.getData(), UserAccount.class);
			if (!StringUtils.isEmpty(tokenUser.getUserUU())) {
				// dialectUID表示client系统自己的唯一标识，比如user_uu，手机号没设置的情况下使用
				authedUser = userService.findUserByUserUU(tokenUser.getUserUU());
			} else if (!StringUtils.isEmpty(tokenUser.getMobile())) {
				// UID表示所有系统公认的唯一标识，这里统一使用手机号
				authedUser = userService.findUserByUserTel(tokenUser.getMobile());
			} else if (!StringUtils.isEmpty(tokenUser.getEmail())) {
				// UID表示所有系统公认的唯一标识，这里统一使用手机号
				authedUser = userService.findUserByUserEmail(tokenUser.getEmail());
			} else {
				logger.error(String.format("invalid user %s, please set uid or dialectUID", tokenUser.getVipName()));
			}
			if (authedUser != null && authedUser.getEnterprises() != null) {
				// 企业资料在client系统自己的唯一标识，比如en_uu
				if (tokenUser.getSpaceUU() != null) {
					authedUser.setCurrentEnterprise(tokenUser.getSpaceUU());
				} else if (StringUtils.isEmpty(tokenUser.getBusinessCode())) {
					for (Enterprise enterprise : authedUser.getEnterprises()) {
						// 企业资料在所有系统公认的唯一标识，这里使用商业登记证号
						if (tokenUser.getBusinessCode().equals(enterprise.getEnBussinessCode())) {
							authedUser.setEnterprise(enterprise);
							break;
						}
					}
				} else { // 如果这两个信息都不存在，判断未登录，因为存在个人账号能登录账户中心的情况
					SystemSession.clear();
					authedUser = null;
				}
			}
		}
		return authedUser;
	}

	@Override
	protected boolean onAuthenticateFailed(HttpServletRequest request, HttpServletResponse response) {
		SystemSession.clear();
		User user = (User) request.getSession().getAttribute("user");
		if (user == null) {
			user = autoLogin(request);
		}
		if (user != null) {
			SystemSession.setUser(user);
			try {
				accessDecision(request, user, response);
			} catch (IOException e) {
				e.printStackTrace();
			}
			return true;
		} else {
			if (SecurityConstant.AUTHENTICATION_URL.equals(request.getRequestURI())) {
				return true;
			}
			AntPathRequestMatcher matcher = new AntPathRequestMatcher("/account/enterprise/info/**");
			if (matcher.matches(request)) {
				return true;
			}
			response.setStatus(HttpStatus.UNAUTHORIZED.value());
			return false;
		}
	}

	@Override
	protected void onAuthenticateSuccess(HttpServletRequest request, HttpServletResponse response) {
		User user = (User) request.getSession().getAttribute("user");
		SSOToken token = SSOHelper.attrToken(request);
		// cookie变化的情况下，session可能还未变化
		if (user == null || (user.getUserTel() != null && !token.getUid().equals(user.getUserTel()))) {
			user = getUserByToken(token);
			if (user != null) {
				user.setIp(AgentUtils.getIp(request));
				request.getSession().setAttribute("user", user);
				setGrantedAuthorities(user);
				log(request, user);
			}
		} else {
			// 从其他应用切换了企业的情况
			if (token.getData() != null) {
				UserAccount tokenUser = FlexJsonUtils.fromJson(token.getData(), UserAccount.class);
				if (!StringUtils.isEmpty(tokenUser.getSpaceUU())
						&& !user.getEnterprise().getUu().equals(tokenUser.getSpaceUU())) {
					user.setCurrentEnterprise(tokenUser.getSpaceUU());
				}
			}
		}
		if (user != null) {
			SystemSession.setUser(user);
			try {
				accessDecision(request, user, response);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 权限验证
	 */
	private void accessDecision(HttpServletRequest request, User user, HttpServletResponse response) throws IOException {
		Collection<ConfigAttribute> configAttributes = getAttributes(request);
		if (null == configAttributes || configAttributes.size() == 0 || user.isSys() || user.getUserUU() < 0) {
			return;
		}
//		if (null == authorities || !authorities.containsKey(user.getUserUU())) {
			setGrantedAuthorities(user);
//		}
		Iterator<ConfigAttribute> iterator = configAttributes.iterator();
		String needPermission = null;
		Collection<GrantedAuthority> userAuthorities = authorities.get(user.getUserUU());
		while (iterator.hasNext()) {
			ConfigAttribute configAttribute = iterator.next();
			needPermission = configAttribute.getAttribute();
			if (userAuthorities != null) {
				for (GrantedAuthority ga : userAuthorities) {
					if (needPermission.equals(ga.getAuthority())) {
						return;
					}
				}
			}
		}
		// 验证是否是当前企业管理员
		for (Role role : user.getRoles()) {
			if (user.getEnterprise().getUu().intValue() == role.getEnUU().intValue() && role.isSys()) {
				return;
			}
		}

		// 验证该用户是否被分配客户
//		List<Vendor> myVendors = userService.findDistribute();
//		if (!CollectionUtils.isEmpty(myVendors)) {
//			return;
//		}
		if (needPermission != null) {
//			if(needPermission.contains("导出")) {
//				String retUrl = request.getContextPath();
//				try {
//					throw new AccessDeniedException("无法访问，没有 " + needPermission + " 权限！");
//				} finally {
////					response.sendRedirect(retUrl + "/static/tpl/start/index.html");
//				}
//			} else {
				throw new AccessDeniedException("无法访问，没有 " + needPermission + " 权限！");
//			}
		}
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
		try {
			super.afterCompletion(request, response, handler, ex);
		} finally {
			SystemSession.clear();
		}
	}

	/**
	 * 加载资源，初始化资源变量
	 * 
	 */
	private void loadResourceDefine() {
		if (resourceMap == null) {
			resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
			List<ResourceItem> resources = resourceItemDao.findAll();
			for (ResourceItem resource : resources) {
				Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();
				ConfigAttribute configAttribute = new SecurityConfig(resource.getName());
				configAttributes.add(configAttribute);
				resourceMap.put(resource.getMethod() + ":" + resource.getUrl(), configAttributes);
			}
		}
	}

	/**
	 * 根据路径获取访问权限的集合接口
	 *
	 * @return
	 * @throws IllegalArgumentException
	 */
	public Collection<ConfigAttribute> getAttributes(HttpServletRequest request) throws IllegalArgumentException {
		if (resourceMap == null) {
            loadResourceDefine();
        }

		for (Iterator<String> iter = resourceMap.keySet().iterator(); iter.hasNext();) {
			String resourceKey = iter.next();
			String[] resourceParam = resourceKey.split(":");
			String resourceMethod = resourceParam[0];
			String resourceUrl = resourceParam[1];
			AntPathRequestMatcher matcher = new AntPathRequestMatcher(resourceUrl);
			if (null != resourceUrl && request.getMethod().equals(resourceMethod) && matcher.matches(request)) {
				return resourceMap.get(resourceKey);
			}
		}
		return null;
	}

	private void setGrantedAuthorities(User user) {
		Set<GrantedAuthority> authSet = new HashSet<GrantedAuthority>();
		user.setCurrentEnterpriseRoles();
		Set<Role> roles = user.getRoles();
		if (!CollectionUtils.isEmpty(roles)) {
			for (Role role : roles) {
				if (role.isSys()) {// 超级账号
					user.setIssys(Constant.YES);
					break;
				}
				Set<ResourceItem> resourceItems = role.getResourceItems();
				if (!CollectionUtils.isEmpty(resourceItems)) {
					for (ResourceItem res : resourceItems) {
						authSet.add(new SimpleGrantedAuthority(res.getName()));
					}
				}
			}
		}
		if (authorities == null) {
			authorities = new HashMap<Long, Collection<GrantedAuthority>>();
		}
		authorities.put(user.getUserUU(), authSet);
	}


	private void log(HttpServletRequest request, User user) {
		// 记录登录日志
		SitePreference preference = getDefaultSitePreferenceForDevice(this.deviceResolver.resolveDevice(request));
		signinLogService.save(new SigninLog(user, preference, true));
	}

	private SitePreference getDefaultSitePreferenceForDevice(Device device) {
		if (device == null) {
			return null;
		}
		if (device.isMobile()) {
			return SitePreference.MOBILE;
		}
		if (device.isTablet()) {
			return SitePreference.TABLET;
		}
		return SitePreference.NORMAL;
	}

	static final String TEL_REGEXP = "^((\\(\\d{3}\\))|(\\d{3}\\-))?(13|15|18)\\d{9}$";

	static final String UU_REGEXP = "^\\d{4,}$";

	/**
	 * 自动登录
	 * 
	 * <pre>
	 * 旧方式
	 * </pre>
	 */
	@Deprecated
	private User autoLogin(HttpServletRequest request) {
		String enUU = request.getParameter("b_enuu");
		String username = request.getParameter("b_username");
		String password = request.getParameter("b_password");
		User user = null;
		if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
			if (username.contains("@")) { // 邮箱登录
				user = userService.findUserByUserEmail(username);
			} else if (username.matches(TEL_REGEXP)) {// 手机号登录
				user = userService.findUserByUserTel(username);
			} else if (username.matches(UU_REGEXP)) {
				user = userService.findUserByUserUU(Long.parseLong(username));
			}
			if (user != null && user.getUserPwd().equals(Md5Utils.encode(password, user.getUserUU()))) {
				checkEnterprise(user, enUU);
				user.setIp(AgentUtils.getIp(request));
				request.getSession().setAttribute("user", user);
				setGrantedAuthorities(user);
			} else {
				throw new UsernameNotFoundException(username + " 账号或密码错误");
			}
		}
		return user;
	}

	private void checkEnterprise(User user, String enUU) {
		boolean choosed = false;
		for (Enterprise enterprise : user.getEnterprises()) {
			if (enterprise.getUu().toString().equals(enUU)) {
				user.setEnterprise(enterprise);
				choosed = true;
				break;
			}
		}
		if (!choosed) {
			throw new UsernameNotFoundException("企业与用户不匹配");
		}
	}

}
