package com.wb.common;

import com.ag.util.Base64Util;
import com.ag.util.StrUtil;
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 (Var.getBool("sys.session.verifyImage.enabled")) {
			if (session == null)
				throw new Exception(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(Str.format(request, "vcInvalid"));
			}
		}
		if (session != null)
			session.invalidate();
		createSession(request);
		if (referer.endsWith("/login") || referer.endsWith("m?xwl=sys/session/login"))
			referer = Var.getString("sys.home");
		if (referer.endsWith("/tlogin") || referer.endsWith("m?xwl=sys/session/tlogin"))
			referer = 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 > Var.limitRecords)
			limit = 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 > Var.limitRecords)
			limit = 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。
	 * mysql版本
	 * @param request 请求对象。
	 * @throws Exception 创建会话失败。
	 */
	@SuppressWarnings("unchecked")
	private static void createSession(HttpServletRequest request) throws Exception {
		int timeout = Var.sessionTimeout;
		HttpSession session;
		String username = request.getParameter("username");
		String password = Base64Util.decode(request.getParameter("password"));
		/*
		String password = request.getParameter("password");*/
		ResultSet rs = (ResultSet) DbUtil
				.run(
						request,
						" select USER_ID,USER_NAME,DISPLAY_NAME,PASSWORD,USE_LANG ,WB_USER.TENANCY_ID, IS_SUPERUSER from WB_USER  where    USER_NAME={?username?} and  STATUS=1");
		if (!rs.next())
			throw new IllegalArgumentException(Str.format(request,
					"userNotExist", username));
		String userId = rs.getString("USER_ID");
		username = rs.getString("USER_NAME");
		String dispname = rs.getString("DISPLAY_NAME");
		String useLang = rs.getString("USE_LANG");
        String tenancyId = rs.getString("TENANCY_ID");
        if (StrUtil.isEmpty(tenancyId)){
        	tenancyId = "default";
		}
//        String ORG_ID = rs.getString("ORG_ID");
//        String SHORT_COD = rs.getString("SHORT_COD");
//        String GOV_CODE = rs.getString("GOV_CODE");
//        String IS_SUPERUSER = rs.getString("IS_SUPERUSER");
//        String NATURE = rs.getString("NATURE");
//        String ORG_NAME = rs.getString("ORG_NAME");

		String status = ResourceBundle.getBundle("system").getString("status");
		if("0".equals(status)) {
			if (!rs.getString("PASSWORD").equals(Encrypter.getMD5(password))) {
				String sql = "INSERT INTO wb_log (LOG_DATE, USER_NAME, IP, LOG_TYPE, MSG) VALUES(now(), {?username?}, '"+request.getRemoteAddr()+"', 2, '用户输入密码错误！')";
				DbUtil.run(request, sql);
				throw new IllegalArgumentException(Str.format(request,
						"passwordInvalid"));
			}
		}
		String isRedisRole = ResourceBundle.getBundle("system").getString("isRedisRole");
		//登录有用户更新权限缓存
		if ("1".equals(isRedisRole)){
			KVBuffer.putRole(username);
		}
		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.TENANCY_ID", tenancyId);//租户
		session.setAttribute("sys.tenancyId", tenancyId);//租户
//        session.setAttribute("sys.ORG_ID", ORG_ID);//组织机构代码
//        session.setAttribute("sys.IS_SUPERUSER", IS_SUPERUSER);//超级用户
//        session.setAttribute("sys.NATURE", NATURE);//用户性质 2码头，5驳船公司 ，4货主
		session.setAttribute("sys.user", userId);//用户主键
//        session.setAttribute("sys.SHORT_COD", SHORT_COD);//用户主键
//        session.setAttribute("sys.GOV_CODE", GOV_CODE);//驳船COD
//        session.setAttribute("sys.ORG_NAME", ORG_NAME);//驳船COD
		session.setAttribute("sys.username", username);//用户显示名
		session.setAttribute("sys.dispname", dispname);//用户登录名
		String dataPermSql = "select USER_NAME from wb_user where DEPT_ID in(select DATA_DEPT_ID from wb_dept_perm where DEPT_ID in (select DEPT_ID from wb_user wu where USER_NAME ='"+username+"'))";
		//数据权限sql，用于可访问的创建人数据，created_by in(dataPermSql)
		session.setAttribute("sys.dataPermSql", dataPermSql);
		ResultSet subs = (ResultSet) DbUtil.run(request, "select * from wb_menu where parent_menu='-1' and menu_type='2' order by order_index");
		StringBuilder sb = new StringBuilder();
		String[] imgs = {"user.png", "order.png", "settle.png", "user.png", "order.png", "settle.png", "user.png", "order.png", "settle.png", "user.png", "order.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 = {?username?}))");
		Map map = new HashMap();
		while (selectUseRole.next()) {
			String url = formatUrl(selectUseRole.getString("MENU_URL"));
			map.put(url, selectUseRole.getString("MENU_NAME"));
		}
		session.setAttribute("selectUseRole", map);
		//得到用户角色信息
		ResultSet useRole = (ResultSet) DbUtil.run(request, "SELECT * FROM wb_user_role  WHERE user_id = {?username?}");
		Map map1 = new HashMap();
		String roleIds = "";
		while (useRole.next()) {
			map1.put(useRole.getString("USER_ID"), useRole.getString("ROLE_ID"));
			roleIds = StrUtil.join(roleIds, useRole.getString("ROLE_ID"), ",");
		}
		//没卵用
		session.setAttribute("useRole",map1);
		//存储用户所有角色
		session.setAttribute("roleIds", ","+roleIds);

		//查询可访问的控件
		String menuSql = "SELECT (SELECT menu_url\n" +
				"            FROM wb_menu\n" +
				"           WHERE menu_id = parent_menu_id)\n" +
				"            url,\n" +
				"         menu_res_ename ename, 0 enabled\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" +
				" union all \n" +
				"SELECT (SELECT menu_url\n" +
				"            FROM wb_menu\n" +
				"           WHERE menu_id = parent_menu_id)\n" +
				"            url,\n" +
				"         menu_res_ename ename,1 enabled\n" +
				"    FROM wb_menu_res\n" +
				"   WHERE menu_res_id  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+"'))";
		ResultSet menuRes = (ResultSet) DbUtil.run(request, menuSql);
		Map mapMenuRes = new HashMap();
		List<Map<String, String>> festivalList = new ArrayList<>();
		while (menuRes.next()){
			Map map2 = new HashMap();
			String url = formatUrl(menuRes.getString("URL"));
			map2.put("url", url);
			map2.put("ename",menuRes.getString("ENAME"));
			map2.put("enabled",menuRes.getString("ENABLED"));
			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());//IP
		session.setAttribute("sys.lang", StringUtil.select(useLang, "auto"));//语言包
		DbUtil
				.run(request,
						"update WB_USER set LOGIN_TIMES=LOGIN_TIMES+1 where USER_ID={?sys.user?}");
		storeUserValues(request, session, userId);
		SimpleListener simpleListener = new SimpleListener();
		if (Var.uniqueLogin) {
			String usernames[] = {username};
			synchronized (lock) {
				removeSession(usernames);
				session.setAttribute("sys.simpleListener", simpleListener);
			}
		} else
			session.setAttribute("sys.simpleListener", simpleListener);
	}

	private static String formatUrl(String url){
		if (url != null && url.contains("m?xwl=")){
			url = url.replace("m?xwl=", "");
		}
		if (url != null && !url.endsWith(".xwl")){
			url += ".xwl";
		}
		return url;
	}
	/**
	 *  存储用户数据至Session的Attribute.
	 * @param session 会话对象。
	 * @throws Exception 存储过程发生异常。
	 */
	private static void storeUserValues(HttpServletRequest request, HttpSession session, String userId)
			throws Exception {
		// 定义需要存储的名称列表，可以根据业务需要扩充列表
		String names[] = 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 (!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");
		}
	}
}
