package com.wb.common;

import com.wb.tool.Encrypter;
import com.wb.util.DbUtil;
import com.wb.util.StringUtil;
import com.wb.util.WebUtil;
import org.json.JSONArray;
import org.json.JSONObject;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.sql.ResultSet;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

public class Session {
	/**
	 * 存放会话的HashMap。该对象仅用于辅助查看当前服务器在线用户列表。
	 * 在集群模式下，默认显示当前服务器的所有会话列表。
	 * 如果需要查看所有服务器的在线用户列表，可以创建实现数据共享Map接口的对象即可，
	 * 如实现Redis Map: userList = new RedisMap();
	 */
	public static Map<String, HashSet<HttpSession>> userList = new ConcurrentHashMap<String, HashSet<HttpSession>>();
	/**
	 * 同步锁，用于执行同步操作。
	 */
	private static Object lock = new Object();

	/**
	 * 验证登录是否合法，合法则成功登录，否则抛出异常。
	 * @param request 请求对象。
	 * @param response 响应对象。
	 * @throws Exception 登录验证过程发生异常。
	 */
	public static void verify(HttpServletRequest request, HttpServletResponse response) throws Exception {
		String referer = StringUtil.opt(request.getHeader("Referer"));
		String redirect = request.getParameter("redirect");
		HttpSession session = request.getSession(false);

		if (com.wb.common.Var.getBool("sys.session.verifyImage.enabled")) {
			if (session == null)
				throw new Exception(com.wb.common.Str.format(request, "vcExpired"));
			String verifyCode = (String) session.getAttribute("sys.verifyCode");
			// 每个验证码只允许一次有效，以防止暴力破解
			session.removeAttribute("sys.verifyCode");
			if (StringUtil.isEmpty(verifyCode) || !StringUtil.isSame(verifyCode, request.getParameter("verifyCode"))) {
				throw new Exception(com.wb.common.Str.format(request, "vcInvalid"));
			}
		}
		if (session != null)
			session.invalidate();
		createSession(request);
		if (referer.endsWith("/login") || referer.endsWith("m?xwl=sys/session/login"))
			referer = com.wb.common.Var.getString("sys.home");
		if (referer.endsWith("/tlogin") || referer.endsWith("m?xwl=sys/session/tlogin"))
			referer = com.wb.common.Var.getString("sys.homeMobile");
		if (StringUtil.isEmpty(redirect))
			WebUtil.send(response, referer);
		else
			response.sendRedirect(redirect);
	}

	/**
	 * 获取指定用户的所有会话列表信息。
	 */
	public static void getSessionList(HttpServletRequest request, HttpServletResponse response) throws Exception {
		String user = request.getParameter("user");
		int start = Integer.parseInt(request.getParameter("start"));
		int limit = Integer.parseInt(request.getParameter("limit"));
		int index = -1, end;
		JSONArray rows = new JSONArray();
		JSONObject row, result;
		HashSet<HttpSession> sessions = userList.get(user);

		if (limit > com.wb.common.Var.limitRecords)
			limit = com.wb.common.Var.limitRecords;
		end = start + limit;
		if (sessions != null) {
			for (HttpSession session : sessions) {
				index++;
				if (index < start)
					continue;
				else if (index >= end)
					break;
				row = new JSONObject();
				row.put("ip", session.getAttribute("sys.ip"));
				row.put("userAgent", session.getAttribute("sys.userAgent"));
				row.put("createDate", new Date(session.getCreationTime()));
				row.put("lastAccessDate", new Date(session.getLastAccessedTime()));
				rows.put(row);
			}
		}
		result = new JSONObject();
		result.put("rows", rows);
		result.put("total", sessions == null ? 0 : sessions.size());
		WebUtil.send(response, result);
	}

	/**
	 * 获取所有在线用户的列表信息。
	 */
	public static void getUserList(HttpServletRequest request, HttpServletResponse response) throws Exception {
		int start = Integer.parseInt(request.getParameter("start"));
		int limit = Integer.parseInt(request.getParameter("limit"));
		int index = -1, end, sessionCount;
		JSONArray rows = new JSONArray();
		JSONObject row, result;
		HashSet<HttpSession> sessions;
		HttpSession session;

		Set<Entry<String, HashSet<HttpSession>>> es = userList.entrySet();
		if (limit > com.wb.common.Var.limitRecords)
			limit = com.wb.common.Var.limitRecords;
		end = start + limit;
		for (Entry<String, HashSet<HttpSession>> e : es) {
			index++;
			if (index < start)
				continue;
			else if (index >= end)
				break;
			sessions = e.getValue();
			sessionCount = 0;
			session = null;
			for (HttpSession sess : sessions) {
				if (session == null)
					session = sess;
				sessionCount++;
			}
			if (sessionCount == 0)
				continue;
			row = new JSONObject();
			row.put("sessionCount", sessionCount);
			row.put("user", e.getKey());
			row.put("username", session.getAttribute("sys.username"));
			row.put("dispname", session.getAttribute("sys.dispname"));
			row.put("ip", session.getAttribute("sys.ip"));
			row.put("userAgent", session.getAttribute("sys.userAgent"));
			rows.put(row);
		}
		result = new JSONObject();
		result.put("rows", rows);
		result.put("total", userList.size());
		WebUtil.send(response, result);
	}

	/**
	 * 注销当前登录用户的会话。
	 * @param request 请求对象
	 * @param response 响应对象
	 * @throws Exception 调用过程发生异常
	 */
	public static void logout(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpSession session = request.getSession(false);
		if (session != null)
			session.invalidate();
	}

	/**
	 * 获取当前用户的角色列表。如果当前用户未登录或未关联角色数据将返回null。
	 * @param request 当前用户关联的请求对象。
	 * @return 角色列表。
	 */
	public static String[] getRoles(HttpServletRequest request) throws Exception {
		HttpSession session = request.getSession(false);
		if (session == null)
			return null;
		else
			return (String[]) session.getAttribute("sys.roles");
	}

	/**
	 * 创建会话。首先验证用户名称和密码是否合法，如果非法抛出异常。如果合法，创建HTTP会话，
	 * 并存储当前用户数据至会话Attribute。
	 * @param request 请求对象。
	 * @throws Exception 创建会话失败。
	 */
	private static void createSession(HttpServletRequest request) throws Exception {
		int timeout = com.wb.common.Var.sessionTimeout;
		HttpSession session;

		String username = request.getParameter("username");
		String password = request.getParameter("password");
		String sqls="select a.USER_ID,a.DISPLAY_NAME,a.PASSWORD,a.USE_LANG from WB_USER a  \n" +
				"where a.user_name={?username?} and STATUS=1  group by a.USER_ID,a.DISPLAY_NAME,a.PASSWORD,\n" +
				"a.USE_LANG ";
		ResultSet rs = (ResultSet) DbUtil
				.run(
						request,
						sqls);
		if (!rs.next())
			throw new IllegalArgumentException(com.wb.common.Str.format(request,
					"userNotExist", username));
		String userId = rs.getString("USER_ID");
		String dispname = rs.getString("DISPLAY_NAME");
		String useLang = rs.getString("USE_LANG");
	//	String tentantid = rs.getString("TENTANTID");
		if (request.getParameter("linkflag")==null || request.getParameter("linkflag").equals("")) {//切换用户 标志，不需要密码判断
			if (request.getParameter("passweb") != null) {
				if (request.getParameter("passweb").equals("true")) {
					if (!rs.getString("PASSWORD").equals(password))
						throw new IllegalArgumentException(com.wb.common.Str.format(request,
								"passwordInvalid"));
				}
			} else if (!rs.getString("PASSWORD").equals(Encrypter.getMD5(password)))
				throw new IllegalArgumentException(Str.format(request,
						"passwordInvalid"));
		}
		session = request.getSession(true);
		// 登录的标志为存在会话且属性sys.logined为非null
		session.setAttribute("sys.logined", true);
		if (timeout == -1)
			timeout = Integer.MAX_VALUE;
		if (timeout > 0)
			session.setMaxInactiveInterval(timeout);
		session.setAttribute("sys.user", userId);
		session.setAttribute("sys.username", username);
		session.setAttribute("sys.dispname", dispname);
		//session.setAttribute("sys.tentantid", tentantid);
		//session.setAttribute("sys.TENANCY_ID", tentantid);

		String sqllistsub="select MENU_ID,MENU_NAME,parent_menu,order_index  from(select distinct MENU_ID,MENU_NAME,parent_menu,order_index " +
				"        from wb_menu  where parent_menu='-1' and menu_type='2'\n" +
				"       start with menu_id in(select distinct wm.menu_id from wb_menu wm,wb_role_resource wrr " +
				"where wm.menu_id=wrr.menu_id and  wrr.role_id in(select role_id from wb_user_role where user_id='"+userId+"'))" +
				"        connect by prior parent_menu=menu_id) order by order_index ";

		ResultSet subs = (ResultSet) DbUtil.run(request,sqllistsub);
		StringBuilder sb = new StringBuilder();
		String[] imgs = {"user.png","order.png","settle.png","settle.png","settle.png","settle.png","settle.png","settle.png","settle.png","settle.png","settle.png"};
		int i=0;
		while (subs.next()){
			sb.append("<li>");
			sb.append("<img src=\"wb/images/photos/"+imgs[i]+"\" style=\"margin-bottom:3px\"/>" +
					"<a onclick=\"subsclick('"+subs.getString("MENU_ID")+"','"+username+"')\">"+subs.getString("MENU_NAME")+"</a>");
			sb.append("</li>");
			if(i==0){

				request.getSession().setAttribute("sys.menuid",subs.getString("MENU_ID"));
			}
			i++;
		}
		session.setAttribute("subsList", sb.toString());

		/**
		 //得到用户权限信息
		 ResultSet selectUseRole = (ResultSet) DbUtil.run(request,"SELECT *\n" +
		 "  FROM wb_menu\n" +
		 " WHERE menu_id IN (SELECT menu_id\n" +
		 "                     FROM wb_role_resource\n" +
		 "                    WHERE role_id in (SELECT role_id\n" +
		 "                                       FROM wb_user_role\n" +
		 "                                      WHERE user_id ='"+userId+"'))");
		 Map map = new HashMap();
		 while (selectUseRole.next()){
		 map.put(selectUseRole.getString("MENU_URL"),selectUseRole.getString("MENU_NAME"));
		 }
		 session.setAttribute("selectUseRole",map);
		 //得到用户角色信息
		 ResultSet useRole = (ResultSet) DbUtil.run(request,"SELECT * FROM wb_user_role  WHERE user_id ='"+userId+"'");
		 Map map1 = new HashMap();
		 while (useRole.next()){
		 map1.put(useRole.getString("USER_ID"),useRole.getString("ROLE_ID"));
		 }
		 session.setAttribute("useRole",map1);
		 */

		//查询资源按钮
		ResultSet menuRes = (ResultSet) DbUtil.run(request,"SELECT (SELECT menu_url\n" +
				"            FROM wb_menu\n" +
				"           WHERE menu_id = parent_menu_id)\n" +
				"            url,\n" +
				"         menu_res_ename ename\n" +
				"    FROM wb_menu_res\n" +
				"   WHERE menu_res_id NOT IN (SELECT menu_res_id\n" +
				"                               FROM wb_menu_res wmr, wb_role_resource wrr\n" +
				"                              WHERE     wmr.menu_res_id = wrr.menu_id\n" +
				"                                    AND role_id IN (SELECT role_id\n" +
				"                                                      FROM wb_user_role\n" +
				"                                                     WHERE user_id = '"+userId+"'))\n" +
				"ORDER BY parent_menu_id");
		Map mapMenuRes = new HashMap();
		List<Map<String, String>> festivalList = new ArrayList<>();
		while (menuRes.next()){
			Map map2 = new HashMap();
			map2.put("url",menuRes.getString("URL"));
			map2.put("ename",menuRes.getString("ENAME"));
			festivalList.add(map2);
		}
		mapMenuRes.put(username,festivalList);
		session.setAttribute("mapMenuRes",mapMenuRes);

		// userAgent最大长度限制为500个字符，防止被注入大字符串
		session.setAttribute("sys.userAgent", StringUtil.substring(request
				.getHeader("user-agent"), 0, 500));
		session.setAttribute("sys.ip", request.getRemoteAddr());
		session.setAttribute("sys.lang", StringUtil.select(useLang, "auto"));
		storeUserValues(request, session, userId);
		com.wb.common.SimpleListener simpleListener = new SimpleListener();
		if (com.wb.common.Var.uniqueLogin) {
			String usernames[] = { username };
			synchronized (lock) {
				//removeSession(usernames);
				session.setAttribute("simpleListener", simpleListener);
			}
		} else
			session.setAttribute("simpleListener", simpleListener);
	}

	/**
	 *  存储用户数据至Session的Attribute.
	 * @param session 会话对象。
	 * @throws Exception 存储过程发生异常。
	 */
	private static void storeUserValues(HttpServletRequest request, HttpSession session, String userId)
			throws Exception {
		// 定义需要存储的名称列表，可以根据业务需要扩充列表
		String names[] = com.wb.common.Var.sessionVars.split(",");
		String valueIds[] = new String[names.length];
		String fieldName, fieldValue, roleArray[];
		ArrayList<String> roles = new ArrayList<String>();
		ResultSet rs;
		int i = 0;

		// 角色
		rs = (ResultSet) DbUtil.run(request,
				"select ROLE_ID from WB_USER_ROLE where USER_ID={?sys.user?} and ROLE_ID<>'default'");
		roles.add("default");// 默认每个用户都具有的角色
		while (rs.next()) {
			roles.add(rs.getString(1));
		}
		roleArray = roles.toArray(new String[roles.size()]);
		session.setAttribute("sys.roles", roleArray);
		session.setAttribute("sys.roleList", StringUtil.join(roleArray, ","));
		// 其他数据
		for (String name : names) {
			valueIds[i++] = StringUtil.concat("'", name, "@", userId, "'");
		}
		rs = (ResultSet) DbUtil.run(request,
				"select VAL_ID,VAL_CONTENT from WB_VALUE where VAL_ID in (" + StringUtil.join(valueIds, ",") + ")");
		while (rs.next()) {
			fieldName = rs.getString("VAL_ID");
			fieldName = StringUtil.substring(fieldName, 0, fieldName.indexOf('@'));
			fieldValue = rs.getString("VAL_CONTENT");
			session.setAttribute("sys." + fieldName, fieldValue);
		}
	}

	/**
	 * 移除指定用户帐户下所有session。
	 * @param userIds 用户id列表。
	 */
	public static void removeSession(String[] userIds) {
		if (!com.wb.common.Var.recordSession)
			return;
		HashSet<HttpSession> sessions;
		HttpSession[] sessionArray;
		int i;

		for (String userId : userIds) {
			sessions = userList.get(userId);
			if (sessions != null) {
				sessionArray = new HttpSession[sessions.size()];
				i = 0;
				for (HttpSession session : sessions) {
					sessionArray[i++] = session;
				}
				// 写入数组再invalid目的是避免HashMap ConcurrentModificationException异常
				for (HttpSession session : sessionArray) {
					session.invalidate();
				}
				userList.remove(userId);
			}
		}
	}

	/**
	 * 更新指定用户关联的session。
	 * @param userId 用户id。
	 * @param roles 角色列表。
	 * @param status 状态，true更新角色，false删除session。
	 */
	public static void updateSession(String userId, String roles[], boolean status) {
		if (!Var.recordSession)
			return;
		HashSet<HttpSession> sessions = userList.get(userId);
		if (sessions != null) {
			if (status) {
				for (HttpSession session : sessions) {
					session.setAttribute("sys.roles", roles);
					session.setAttribute("sys.roleList", StringUtil.join(roles, ","));
				}
			} else {
				String[] userIds = { userId };
				removeSession(userIds);
			}
		}
	}
	/**
	 * 校验用户密码是否正确。
	 *
	 * @param request 请求对象。
	 * @throws Exception 创建会话失败。
	 */
	public static void verifyPass(HttpServletRequest request, HttpServletResponse response) throws Exception {

		String username = request.getParameter("username");
		String password = request.getParameter("password");
		String sqls = "select a.PASSWORD from WB_USER a  \n" +
				"where a.user_name={?username?} ";
		ResultSet rs = (ResultSet) DbUtil.run( request, sqls);
		String dpass = "";
		while (rs.next()) {
			dpass = rs.getString("PASSWORD");
		}
		if (dpass.equals(Encrypter.getMD5(password))){
			WebUtil.send(response, "OK");
		}else {
			WebUtil.send(response, "NO");
		}
	}
}
