package com.mortals.xhx.base.login.web;

import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Set;

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

import com.mortals.xhx.base.system.menu.model.MenuEntity;
import com.mortals.xhx.base.system.menu.service.MenuService;
import com.mortals.xhx.base.system.resource.service.ResourceService;
import com.mortals.xhx.base.system.user.model.UserEntity;
import com.mortals.xhx.base.system.user.service.UserService;
import com.mortals.xhx.base.system.user.web.UserForm;
import com.mortals.xhx.base.system.valid.service.ValidCodeService;
import com.mortals.xhx.common.key.RedisKey;
import com.mortals.xhx.module.site.service.SiteService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSONObject;
import com.mortals.framework.ap.CookieService;
import com.mortals.framework.ap.GlobalSysInfo;
import com.mortals.framework.ap.SysConstains;
import com.mortals.framework.model.CookieInfo;
import com.mortals.framework.service.ICacheService;
import com.mortals.framework.service.ITokenService;
import com.mortals.framework.service.IUser;
import com.mortals.framework.util.AESUtil;
import com.mortals.framework.util.DataUtil;
import com.mortals.framework.util.HttpUtil;
import com.mortals.framework.util.MD5Util;
import com.mortals.framework.util.StringUtils;
import com.mortals.framework.web.BaseCRUDJsonController;

@RestController
@RequestMapping("login")
public class LoginController extends BaseCRUDJsonController<UserService, UserForm, UserEntity, Long> {

    @Autowired
    private UserService userService;
    @Autowired
    private ValidCodeService validCodeService;
    @Autowired
    private ResourceService resourceService;
    @Autowired
    private MenuService menuService;
    @Autowired
    private ITokenService tokenService;
    @Autowired
    private ICacheService cacheService;
    @Autowired
    private SiteService siteService;

    private static Log logger = LogFactory.getLog(LoginController.class);

    @RequestMapping("login")
    public String login(HttpServletRequest request, HttpServletResponse response, LoginForm loginForm) throws Exception {

        JSONObject ret = new JSONObject();
        String loginName = loginForm.getLoginName();
        String password = loginForm.getPassword();
        String securityCode = loginForm.getSecurityCode();
        String ip = super.getRequestIP(request);
        if (StringUtils.isEmpty(loginName) || StringUtils.isEmpty(password)) {
            ret.put(KEY_RESULT_CODE, VALUE_RESULT_FAILURE);
            ret.put(KEY_RESULT_MSG, "未获取到用户信息，请重新登录");
            return ret.toJSONString();
        }
        UserEntity userEntity = null;
        try {
            loginForm.validate();
            boolean result = validCodeService.doCheckImageValidCode(request.getSession().getId(), ip, securityCode);
            if (!result) {
                recordSysLog(request, userEntity, "登录验证码不正确或已过期!");
                ret.put(KEY_RESULT_CODE, VALUE_RESULT_FAILURE);
                ret.put(KEY_RESULT_MSG, "登录验证码不正确或已过期!");
                return ret.toJSONString();
            }
            userEntity = userService.doLogin(loginName, password, ip);
            userEntity.setLastLoginAddress(ip);
            saveCurrUser(request, response, userEntity);
            recordSysLog(request, userEntity, "用户登录系统成功!");
            // 返回拥有的菜单数据
            Set<String> urls = resourceService.findUrlSetByUserId(userEntity.getId());
            List<MenuEntity> outlookBarList = menuService.findTreeMenu(userEntity, urls);
            String currUserName = userEntity.getRealName();
            if (currUserName == null || currUserName.trim().length() == 0) {
                currUserName = "管理员";
            }
            JSONObject data = new JSONObject();
            data.put("currUserName", currUserName);
            data.put("barList", outlookBarList);
            data.put("id", userEntity.getId());
            data.put("userType", userEntity.getUserType());

            this.generateBlackCookie(request, response, loginName, urls);
            ret.put(KEY_RESULT_DATA, data);
            ret.put(KEY_RESULT_CODE, VALUE_RESULT_SUCCESS);
            ret.put(KEY_RESULT_MSG, "用户登录系统成功!");
            ret.put("resources", urls);

            return ret.toJSONString();
        } catch (Exception e) {
            log.error("login error ", e);
            if (userEntity == null) {
                userEntity = new UserEntity();
                userEntity.setLoginName(loginName);
            }
            ret.put(KEY_RESULT_CODE, VALUE_RESULT_FAILURE);
            ret.put(KEY_RESULT_MSG, super.convertException(e));
            return ret.toJSONString();
        }
    }

    @RequestMapping("logout")
    public void logout(HttpServletRequest request, HttpServletResponse response) throws Exception {
        recordSysLog(request, "退出登录");
        super.removeCurrUser(request, response);
        this.deleteBlackCookie(request, response);
    }

    @RequestMapping("index")
    public String index(HttpServletRequest request, HttpServletResponse response) throws Exception {
        JSONObject ret = new JSONObject();
        IUser user = this.getCurUser();
        if (user == null) {
            return "";
        }
        Set<String> urls = resourceService.findUrlSetByUserId(user.getId());
        List<MenuEntity> outlookBarList = menuService.findTreeMenu(user, urls);
        String currUserName = user.getRealName();
        if (currUserName == null || currUserName.trim().length() == 0) {
            currUserName = "管理员";
        }
        JSONObject data = new JSONObject();
        data.put("currUserName", currUserName);
        data.put("barList", outlookBarList);
        data.put("id", user.getId());
        data.put("userType", user.getUserType());
        ret.put(KEY_RESULT_DATA, data);
        this.generateBlackCookie(request, response, user.getLoginName(), urls);
        ret.put(KEY_RESULT_CODE, VALUE_RESULT_SUCCESS);
        ret.put(KEY_RESULT_MSG, "用户登录系统成功!");
        ret.put("resources", urls);
        return ret.toJSONString();
    }

    private void generateBlackCookie(HttpServletRequest request, HttpServletResponse response, String loginName, Set<String> urls) {
        try {
            String cacheKey = RedisKey.KEY_MENU_CACHE + loginName;
            String securityKey = GlobalSysInfo.getPropertyValue(SysConstains.PROP_COOKIE_SECURITY_KEY);
            //应为Cookie会超长，所以改为仅存储key将值放入redis
            //CookieService.setCookieForAuth(request, response, securityKey, null);
            StringBuilder sb = new StringBuilder();
            if (urls != null && urls.size() > 0) {
                for (String url : urls) {
                    int index = url.hashCode() & (Integer.MAX_VALUE - 1);
                    sb.append(index).append(",");
                }
            }
            String menuUrl = sb.toString();
            menuUrl = AESUtil.encrypt(menuUrl, securityKey);
            cacheService.set(cacheKey, menuUrl, 604800);
            HttpUtil.setCookieValue(request, response, SysConstains.COOKIE_MENU, cacheKey, -1);

        } catch (Throwable e) {

        }
    }

    @RequestMapping("validcode")
    public String validCode(HttpServletRequest request, HttpServletResponse response, LoginForm loginForm) {
        JSONObject ret = new JSONObject();
        String loginName = loginForm.getLoginName();
        String password = loginForm.getPassword();
        if (StringUtils.isEmpty(loginName) || StringUtils.isEmpty(password)) {
            ret.put(KEY_RESULT_CODE, VALUE_RESULT_FAILURE);
            ret.put(KEY_RESULT_MSG, "登录名或密码为空");
        }
        UserEntity user = null;
        try {
            user = userService.doCheckUser(loginName, password);
            if (user == null) {
                ret.put(KEY_RESULT_CODE, VALUE_RESULT_FAILURE);
                ret.put(KEY_RESULT_MSG, "用户名或密码不正确");
            } else if (StringUtils.isEmpty(user.getMobile())) {
                ret.put(KEY_RESULT_CODE, VALUE_RESULT_FAILURE);
                ret.put(KEY_RESULT_MSG, "用户无正确的手机号");
            } else {
                String mobile = StringUtils.trim(user.getMobile());
                String sessionId = request.getSession().getId();
                String ip = super.getRequestIP(request);
                boolean result = validCodeService.createSmsValidCode(sessionId, ip, mobile);
                if (result) {
                    ret.put(KEY_RESULT_CODE, VALUE_RESULT_SUCCESS);
                } else {
                    ret.put(KEY_RESULT_CODE, VALUE_RESULT_FAILURE);
                    ret.put(KEY_RESULT_MSG, "验证码发送失败，请稍后在试！");
                }
            }
        } catch (Exception e) {
            ret.put(KEY_RESULT_CODE, VALUE_RESULT_FAILURE);
            ret.put(KEY_RESULT_MSG, "验证码发送失败：" + super.convertException(e));
        } finally {
            if (user == null) {
                user = new UserEntity();
                user.setLoginName(loginName);
            }
            recordSysLog(request, user, "用户获取短信验证码");
        }
        return ret.toJSONString();
    }

    protected void deleteBlackCookie(HttpServletRequest request, HttpServletResponse response) {
        try {
            CookieService.deleteCookieForAuth(request, response);
        } catch (Throwable e) {
        }
    }

    private static CookieInfo setLoginCookie(HttpServletResponse response, IUser user, String securityKey,
                                             String cookieDomain, int cookieMaxAge) {
        try {
            //设置cookie有效串，防篡改   LTPA版本号+创建时间+过期时间+用户名+LTPA密钥
            //          SHA-1=LTPA版本号+创建时间+过期时间+用户名+Domino LTPA 密钥
            //          LTPA Cookie= Base64(LTPA版本号+创建时间+过期时间+用户名+SHA-1)
            Calendar cal = Calendar.getInstance();
            long currTime = cal.getTime().getTime() / 1000;
            cal.add(Calendar.DAY_OF_YEAR, 1);
            long expiresTime = cal.getTime().getTime() / 1000;
            Long userId = user.getId();
            int userType = user.getUserType();
            String loginName = user.getLoginName();
            String userName = user.getRealName();
            Long customerId = user.getCustomerId();
            Long customerJoinId = user.getCustomerJoinId();
            String customerNum = user.getCustomerNum();
            String customerJson = "";
            Long deptId = user.getDeptId();
            Long roleId = 0L;
            Long siteId = user.getSiteId();
            String deptName = user.getDeptName();
            int deptManager = user.isManager() ? 1 : 0;
            if (customerId != null || customerJoinId != null || StringUtils.isNotEmpty(customerNum)) {
                try {
                    customerJson = DataUtil.conver2Long(customerId) + CookieService.COOKIE_STORE_SPLIT
                            + DataUtil.conver2Long(customerJoinId) + CookieService.COOKIE_STORE_SPLIT
                            + StringUtils.trim(customerNum) + CookieService.COOKIE_STORE_SPLIT
                            + DataUtil.conver2Int(deptId) + CookieService.COOKIE_STORE_SPLIT
                            + StringUtils.trim(deptName) + CookieService.COOKIE_STORE_SPLIT
                            + deptManager + CookieService.COOKIE_STORE_SPLIT
                            + roleId + CookieService.COOKIE_STORE_SPLIT
                    ;
                    customerJson = AESUtil.encrypt(customerJson, securityKey);
                } catch (Exception e) {
                    logger.debug("用户的关联信息加密异常-->" + e.getMessage());
                }
            }
            customerJson = StringUtils.trim(customerJson);
            String cookieStroe = CookieService.COOKIE_STORE_VERSION + CookieService.COOKIE_STORE_SPLIT + currTime
                    + CookieService.COOKIE_STORE_SPLIT + expiresTime;
            String pinId = AESUtil.encrypt(userId.toString() + CookieService.COOKIE_STORE_SPLIT + userType+ CookieService.COOKIE_STORE_SPLIT + siteId,
                    securityKey);
            String trackId = MD5Util.getSignAndMD5(cookieStroe, pinId, loginName, userName, customerJson, securityKey);
            HttpUtil.setCookieValue(response, SysConstains.COOKIE_PIN, loginName, cookieDomain, cookieMaxAge);
            HttpUtil.setCookieValue(response, SysConstains.COOKIE_PIN_ID, pinId, cookieDomain, cookieMaxAge);
            HttpUtil.setCookieValue(response, SysConstains.COOKIE_UNICK, userName, cookieDomain, cookieMaxAge);
            HttpUtil.setCookieValue(response, SysConstains.COOKIE_STORE, cookieStroe, cookieDomain, cookieMaxAge);
            HttpUtil.setCookieValue(response, SysConstains.COOKIE_STORE_TOKEN, trackId, cookieDomain, cookieMaxAge);
            if (StringUtils.isNotEmpty(customerJson)) {
                HttpUtil.setCookieValue(response, SysConstains.COOKIE_UNION, customerJson, cookieDomain, cookieMaxAge);
            }
            CookieInfo info = new CookieInfo(trackId, new Date(currTime), new Date(expiresTime), user);
            return info;
        } catch (Throwable e) {
            logger.warn("设置用户的cookie异常-->" + e.getMessage());
            return null;
        }
    }

    @Override
    protected void saveCurrUser(HttpServletRequest request, HttpServletResponse response, IUser user, int maxAge) {
        String securityKey = GlobalSysInfo.getPropertyValue(SysConstains.PROP_COOKIE_SECURITY_KEY);
        String cookieDomain = GlobalSysInfo.getPropertyValue(SysConstains.PROP_COOKIE_DOMAIN);
        String currDoman = request.getServerName();
        CookieInfo cookie = setLoginCookie(response, user, securityKey, currDoman, maxAge);
        if (StringUtils.isNotEmpty(cookieDomain) && !currDoman.equals(cookieDomain)) {
            setLoginCookie(response, user, securityKey, cookieDomain, maxAge);
        }
        if (cookie != null && tokenService != null) {
            try {
                tokenService.saveToken(cookie);
            } catch (Throwable e) {
                log.error("存储cookie信息到服务器异常", e);
            }
        }

    }
}
