    package com.pqs9000.controller.user;

import com.njcn.enums.CookieKeyEnum;
import com.njcn.enums.LogTypeEnum;
import com.njcn.enums.ProjectEnum;
import com.njcn.mapper.user.ManageListMapper;
import com.njcn.mapper.user.UserMapper;
import com.njcn.mapper.user.UserSetMapper;
import com.njcn.pojo.commons.AreaInfo;
import com.njcn.pojo.commons.HttpResult;
import com.njcn.pojo.commons.RedisDB;
import com.njcn.pojo.user.ManageList;
import com.njcn.service.log.UserLogDetailService;
import com.njcn.shiro.filter.KickoutSessionFilter;
import com.njcn.sso.pojo.user.User;
import com.njcn.service.log.UserLoginService;
import com.njcn.service.user.UserService;
import com.njcn.shiro.session.UserShiroSessionRepository;
import com.njcn.shiro.session.impl.JedisShiroSessionRepository;
import com.njcn.shiro.token.TokenManager;
import com.njcn.utils.*;
import com.njcn.utils.redis.JedisManager;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Param;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.MalformedURLException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.*;

/**
 * 管理用户的登录、增删改查、验证码获取等。
 */
@Controller
@RequestMapping("user")
public class UserController {

    @Autowired
    private UserService us;

    @Autowired
    UserUtil userUtil;

    @Autowired
    UserSetMapper usm;

    @Autowired
    UserMapper um;

    @Autowired
    UserLoginService userLoginService;

    @Autowired
    UserLogDetailService userLogservice;

    @Autowired
    JedisManager jedisManager;

    @Autowired
    private AppConfig appConfig;

    @Autowired
    ManageListMapper alm;

    @Autowired
    private UserShiroSessionRepository userShiroSessionRepository;

    // 日志记录
    private static final Logger logger = LoggerFactory.getLogger(UserController.class);

    /**
     * 根据当前用户获取区域选择tree
     */
    @PostMapping("getAreasInfo")
    @ResponseBody
    public List<AreaInfo> getAreasInfo(){
        List<AreaInfo> areaInfos;
        try{
            areaInfos=us.getAreasByUser();
            if(CollectionUtils.isEmpty(areaInfos)){
                areaInfos=null;
            }
        }catch (Exception e){
            areaInfos=null;
        }
        return areaInfos;
    }


    @PostMapping("clear")
    @ResponseBody
    public String clear(){
        TokenManager.clearNowUserAuth();
        return "";
    }


    /**
     * 用户注销，统一重定向到sso中心，在sso中心查询该用户所有对应的sessionID，进行缓存清理，以达到子系统全部注销
     */
    @GetMapping("loginout")
    public void loginOut(HttpServletRequest request, HttpServletResponse response) throws IOException {
        boolean flag = appConfig.isOneContainer();
        String url;
        if (flag) {
            String baseUrl = request.getRequestURL().toString();
            url = baseUrl.substring(0, baseUrl.indexOf(ProjectEnum.PQS9000.getItem())) + ProjectEnum.PQS9000.getLoginOut();
        } else {
            url = appConfig.getSsoUrl() + "/loginout?item=pqs9000";
        }
        WebUtils.issueRedirect(request, response, url);
    }


    /**
     * sso认证完成后，访问子系统，任然需要注册子系统，包括当前用户在子系统下的权限
     */
    @GetMapping("ssoRegister")
    public void ssoRegister(HttpServletRequest request, HttpServletResponse response,String token) throws IOException {
        //获取父容器的sessionID，即token值
        if (StringUtils.isBlank(token)) {
            redirectSso(request, response);
        } else {
            //首先根据token去redis中查询有没有对应的session
            Session session = userShiroSessionRepository.getSession(token);
            //没有需要的共享会话，则重定向回登录中心
            if (null == session) {
                redirectSso(request, response);
            } else {
                //判断认证中心的会话是否被T，被T，子系统是不允许注册的，返回认证中心登录页面
                Boolean marker = (Boolean) session.getAttribute(KickoutSessionFilter.KICKOUT_STATUS);
                if (null != marker && marker) {
                    redirectSso(request, response);
                }
                //获取session登录信息。
                Object obj = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
                if (null == obj) {
                    //没有用户信息，则重定向回登录中心
                    redirectSso(request, response);
                } else {
                    SimplePrincipalCollection spc = (SimplePrincipalCollection) obj;
                    obj = spc.getPrimaryPrincipal();
                    if (null == obj) {
                        //没有用户信息，则重定向回登录中心
                        redirectSso(request, response);
                    } else {
                        User ssoUser = (User) obj;
                        try {
                            TokenManager.login(ssoUser.getLoginName(),"",ssoUser.getPassword(),PubUtils.getIpAddr(request),ProjectEnum.PQS9000.getItem());
                            try {
                                userLoginService.insertLogin(ssoUser.getLoginName(), ProjectEnum.PQS9000.getItem());
                            } catch (Exception e) {
                                logger.error("用户登录成功后，保存记录出错。原因：" + e.toString());
                            }
                            saveSessionByUser(session,token, TokenManager.getUserId(), TokenManager.getSession().getId().toString());
                            userShiroSessionRepository.activeSession(session);
                            TokenManager.getSession().setAttribute(CookieKeyEnum.SSOLOGIN.getCookieKey(), token);
                            userUtil.saveRandomToken();
                            //认证成功重定向到业务主界面
                            WebUtils.issueRedirect(request, response, "/business/doBusiness");
                        } catch (Exception u) {
                            logger.error("子系统pqs9000注册时异常，异常为：" + response.toString());
                        }
                    }
                }
            }
        }

    }




    /**
     * 重定向到sso认证中心，重新登录
     */
    private void redirectSso(HttpServletRequest request, HttpServletResponse response) throws IOException {
        boolean flag = appConfig.isOneContainer();
        String url;
        if (flag) {
            String baseUrl = request.getRequestURL().toString();
            url = baseUrl.substring(0, baseUrl.indexOf(ProjectEnum.PQS9000.getItem())) +ProjectEnum.SSO.getLoginUrl()+ProjectEnum.PQS9000.getItem() ;
        } else {
            url = appConfig.getSsoJudge();
        }
        WebUtils.issueRedirect(request, response, url);
    }

    /**
     * 根据用户索引，保存当前系统的sessionID，以便注销系统时删除会话信息
     *
     * @param token  认证中心会话索引
     * @param userId    用户索引
     * @param sessionId 会话ID
     */
    private void saveSessionByUser(Session session,String token, String userId, String sessionId) {
        String key=token+userId;
        List<String> infoMap= (List<String>) session.getAttribute(key);
        //如果不存在，创建一个新的
        infoMap = null == infoMap ? new ArrayList<>() : infoMap;
        infoMap.add(JedisShiroSessionRepository.REDIS_SHIRO_SESSION + sessionId);
        session.setAttribute(key,infoMap);
    }


    /**
     * foundChrome
     * 判断浏览器在本地是否存在
     *
     * @throws FileNotFoundException
     */
    @RequestMapping(value = "foundChrome", method = RequestMethod.POST)
    @ResponseBody
    public HttpResult foundChrome(HttpServletResponse response, @Param(value = "res_Index") String res_Index) {
        ManageList list = new ManageList();
        list.setRes_Index(res_Index);
        list.setState(1);
        list.setSysType(appConfig.getSysType());
        List<ManageList> manageLists = alm.select(list);
        return PubUtils.foundChrome(manageLists);
    }

    /**
     * downloadChrome
     * 用户下载谷歌浏览器安装版
     * 存在D盘，配置文件配置源路径
     *
     * @throws FileNotFoundException
     * @throws MalformedURLException
     */
    @RequestMapping(value = "downloadChrome", method = RequestMethod.GET)
    public void downloadChrome(HttpServletResponse response, @Param(value = "res_Index") String res_Index, HttpServletRequest request){
        ManageList list = new ManageList();
        list.setRes_Index(res_Index);
        list.setState(1);
        list.setSysType(appConfig.getSysType());
        List<ManageList> manageLists = alm.select(list);
        PubUtils.downloadChrome(response,manageLists);
    }


    /**
     * 用户没有权限提示的页面
     */
    @GetMapping(value = "unauthorized")
    public String unauthorized() {
        return "auth/unauthorized";
    }



    /************************
     * RSA
     * 根据用户名生成秘钥对
     * @param username 用户名
     *************************/
    @PostMapping("generateRSAKey")
    @ResponseBody
    public HttpResult generateRSAKey(String username, HttpServletRequest request) {
        HttpResult result;
        try {
            username = DesUtils.aesDecrypt(username);
            // 获取公钥和私钥
            HashMap<String, Object> keys = RSAUtils.getKeys();
            RSAPublicKey publicKey = (RSAPublicKey) keys.get("public");
            RSAPrivateKey privateKey = (RSAPrivateKey) keys.get("private");
            String ip = StringUtils.isBlank(PubUtils.getIpAddr(request)) ? "127.0.0.1" : PubUtils.getIpAddr(request);
            //秘钥先删除再添加
            jedisManager.deleteByKey(RedisDB.SHORT_TIME_QUERY, SerializeUtil.serialize(username + ip));
            // 保存私钥到 redis，也可以保存到数据库
            jedisManager.saveValueByKey(RedisDB.SHORT_TIME_QUERY, SerializeUtil.serialize(username + ip), SerializeUtil.serialize(privateKey), -1);
            // 将公钥传到前端
            Map<String, String> map = new HashMap<String, String>();
            // 注意返回modulus和exponent以16为基数的BigInteger的字符串表示形式
            map.put("modulus", publicKey.getModulus().toString(16));
            map.put("exponent", publicKey.getPublicExponent().toString(16));
            result = PubUtils.assignmentResult(map, 200, "生成秘钥对成功！");


        } catch (Exception u) {
            result = PubUtils.assignmentResult(null, 500, u.getMessage());
        }
        return result;
    }


    /************************
     * SM2
     * 根据用户名生成秘钥对
     * @param username 用户名
     *************************/
    @PostMapping("generateSM2Key")
    @ResponseBody
    public HttpResult generateSM2Key(String username, HttpServletRequest request) {
        HttpResult result;
        try {
            if (PubUtils.isBlank(username)) {
                result = PubUtils.assignmentResult(null, 500, "用户名非法！");
                return result;
            }
            username = DesUtils.aesDecrypt(username);
            // 获取公钥和私钥
            Map<String, String> keyMap =PubUtils.getSM2CipHer();
            String publicKey = keyMap.get("publicKey");  //加密密钥
            String privateKey = keyMap.get("privateKey"); //解密密钥
            String ip = PubUtils.isBlank(PubUtils.getIpAddr(request)) ? "127.0.0.1" : PubUtils.getIpAddr(request);
            //秘钥先删除再添加
            jedisManager.deleteByKey(RedisDB.SHORT_TIME_QUERY, SerializeUtil.serialize(username + ip));
            // 保存私钥到 redis，也可以保存到数据库
            jedisManager.saveValueByKey(RedisDB.SHORT_TIME_QUERY, SerializeUtil.serialize(username + ip), SerializeUtil.serialize(privateKey), -1);
            // 将公钥传到前端
            result = PubUtils.assignmentResult(publicKey, 200, "生成秘钥对成功！");
        } catch (Exception u) {
            result = PubUtils.assignmentResult(null, 500, u.getMessage());
        }
        return result;
    }

    /**
     * 确认密码
     */
    @PostMapping(value = "confirmPwd")
    @ResponseBody
    public HttpResult confirmPwd(String password, HttpServletRequest request,HttpServletResponse response) {
        String ip = PubUtils.getIpAddr(request);
        String username = TokenManager.getToken().getLoginName();
        password = XssFilterUtil.dealString(password);
        HttpResult result=PubUtils.initResult(TokenManager.getToken().getLoginName(),request, LogTypeEnum.SYSTEM.toString(),2);
        try {
            //密码处理
            String privateKey = jedisManager.getValueByKey(RedisDB.SHORT_TIME_QUERY, username + ip);
            //秘钥用完即删
            jedisManager.deleteByKey(RedisDB.SHORT_TIME_QUERY, username + ip);
            //对SM2解密面进行验证
            password = PubUtils.getPasswordSM2Verify(privateKey, password);
            if (password == null && password.length() == 0) {
                return PubUtils.assignmentResultLog(null, 500, "密码传输完整性被破坏","密码确认","失败",result);
            }
            us.judgeUsernameAndPwd(username, password);
            response.setStatus(203);
            result = PubUtils.assignmentResultLog(null, 200, "密码正确","密码确认","成功",result);
            //缓存验证结果
            jedisManager.saveValueByKey(RedisDB.SHORT_TIME_QUERY, SerializeUtil.serialize(username + ip), SerializeUtil.serialize("success"), -1);
        } catch (Exception e) {
            if(e.getMessage().length()>12){
                result = PubUtils.assignmentResultLog(null, 500, "密码确认异常","密码确认","失败",result);
            }else{
                 result = PubUtils.assignmentResultLog(null, 500, e.getMessage(),"密码确认","失败",result);
            }
            //缓存验证结果
            jedisManager.saveValueByKey(RedisDB.SHORT_TIME_QUERY, SerializeUtil.serialize(username + ip), SerializeUtil.serialize("fail"), -1);
        }
        return result;
    }

    /**
     * 根据用户索引获取用户信息回显，以便修改
     *
     * @param userIndex 用户索引
     */
    @ResponseBody
    @PostMapping(value = "selectId")
    public User selectId(String userIndex, int flag) {
        return us.selectId(userIndex, flag);
    }

    //修改密码
    @ResponseBody
    @RequestMapping(value = "updatePassword", method = RequestMethod.POST)
    public HttpResult updatePassword(HttpServletRequest request, @Param(value = "username") String username,
                                     @Param(value = "passwordX") String passwordX,
                                     String token) {
        HttpResult result = new HttpResult();

//        String sessionToken = (String) TokenManager.getSession().getAttribute("token");
//        if (!token.equalsIgnoreCase(sessionToken)) {
//            result = PubUtils.assignmentResult(null, 500, "非法访问");
//            return result;
//        }
        //username = XssFilterUtil.dealString(username);
        username = DesUtils.aesDecrypt(username);
        passwordX = XssFilterUtil.dealString(passwordX);

        String ip = StringUtils.isBlank(PubUtils.getIpAddr(request)) ? "127.0.0.1" : PubUtils.getIpAddr(request);
        try {
            //密码处理
            String privateKey = jedisManager.getValueByKey(RedisDB.SHORT_TIME_QUERY, username + ip);
            //秘钥用完即删
            jedisManager.deleteByKey(RedisDB.SHORT_TIME_QUERY, username + ip);
            //对SM2解密面进行验证
            passwordX = PubUtils.getPasswordSM2Verify(privateKey, passwordX);
            if (passwordX == null && passwordX.length() == 0) {
                result = PubUtils.assignmentResult(null, 500, "密码传输完整性被破坏");
                userLogservice.getLog("修改用户密码", "失败", PubUtils.getIpAddr(request), LogTypeEnum.SYSTEM.toString(), 1);
                return result;
            }

            //增加判断密码是否包含用户名
            if (passwordX.indexOf(username) > -1) {
                userLogservice.getLog("修改用户密码", "失败", PubUtils.getIpAddr(request), LogTypeEnum.SYSTEM.toString(), 1);
                return PubUtils.assignmentResult(null, 500, "用户密码包含登录名");
            }

            if (!(PubUtils.patternPassword(passwordX) || PubUtils.patternUsername(username))) {
                userLogservice.getLog("修改用户密码", "失败", PubUtils.getIpAddr(request), LogTypeEnum.SYSTEM.toString(), 1);
                result = PubUtils.assignmentResult(null, 500, "用户名、密码格式不正确");
                return result;
            }
            try {
                us.modifyPassword(username, passwordX);
                userLogservice.getLog("修改用户密码", "成功", PubUtils.getIpAddr(request), LogTypeEnum.SYSTEM.toString(), 1);
                result = PubUtils.assignmentResult(null, 200, "修改密码成功");
                User user = new User();
                user.setLoginName(username);
                user = um.selectOne(user);
                if (null != user) {
                    user.setState(1);
                    um.updateByPrimaryKeyBySql(user);
                }
            } catch (Exception e) {
                userLogservice.getLog("修改用户密码", "失败", PubUtils.getIpAddr(request), LogTypeEnum.SYSTEM.toString(), 1);
                result = PubUtils.assignmentResult(null, 500, e.getMessage());
                logger.error("用户修改密码成功后，保存记录出错。原因：" + e.toString());
            }
        } catch (Exception e) {
            logger.error("更新密码出错，异常为：" + e.toString());
            String eMessage = e.getMessage();
            if (eMessage.length() > 20) {
                eMessage = "内部出错";
            }
            result = PubUtils.assignmentResult(null, 500, eMessage);
            try {
                userLogservice.getLog("修改用户密码", "失败", PubUtils.getIpAddr(request), LogTypeEnum.SYSTEM.toString(), 1);
            } catch (Exception u) {
                logger.error("用户修改密码失败后，保存记录出错。原因：" + u.toString());
            }
        }
        return result;
    }
}
