package com.ruoyi.framework.interceptor;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ruoyi.common.annotation.Security;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.RedisUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.ip.AddressUtils;
import com.ruoyi.framework.web.service.SysPermissionService;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.zhdd.domain.GatewayUserDetail;
import com.ruoyi.zhdd.feign.FeignUserManageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLDecoder;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @Description: TODO
 * @Author: huangcheng
 * @Date: 2022/2/23
 * @Version V1.0
 */
@Component
@Slf4j
public class SecurityInterceptor implements HandlerInterceptor {

    @Autowired
    private ISysUserService userService;
    @Autowired
    private SysPermissionService permissionService;
    @Autowired
    private FeignUserManageService feignUserManageService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        /**
         * handler保存了本次请求的controller也就是接口方法的一些信息，如类，方法，参数等
         * 如果是一次静态资源的请求则该handler不应该是HandlerMethod的实现类
         * 判断是否是一个正常的接口，如果是则进行鉴权操作，不是则直接放行
         */
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;// 把handler强转为HandlerMethod
            // 从handlerMethod中获取本次请求的接口方法对象然后判断该方法上是否标有我们自定义的注解@Security
            Security security = handlerMethod.getMethod().getAnnotation(Security.class);
            if (null != security) {
                // 对用户进行鉴权
                String userStr = request.getHeader("user");
                if (StrUtil.isBlank(userStr)) {
                    throw new ServiceException("请重新登录系统！", 90000);
                }
                userStr = URLDecoder.decode(userStr, "UTF-8");
                JSONObject userJsonObject = JSONUtil.parseObj(userStr);
                String id = userJsonObject.getStr("id");
                log.info("userJsonObject:{}", userJsonObject);
                // 先从缓存中获取
                String userKey = Constants.LOGIN_TOKEN_KEY + id;
                LoginUser user = RedisUtils.getCacheObject(userKey);
                if (user != null) {
                    RedisUtils.expire(userKey, 120, TimeUnit.MINUTES);
                    return true;
                }
                // 如果缓存中没有的话，则先查询我们的数据库是否存在，如果不存在则查询四维用户是否存在
                SysUser sysUser = userService.selectUserById(id);
                if (sysUser != null) {
                    if (StrUtil.isBlank(sysUser.getDeptId())) {
                        throw new ServiceException("请联系应急事件处置系统管理员分配组织", 90001);
                    }
                    // 查询当前用户角色
                    SysUser userRole = new SysUser();
                    userRole.setUserId(id);
                    Set<String> roles = permissionService.getRolePermission(userRole);
                    if (roles.isEmpty()) {
                        throw new ServiceException("请联系应急事件处置系统管理员分配权限", 90002);
                    }
                    user = new LoginUser(sysUser.getUserId(), sysUser.getDeptId(), sysUser, permissionService.getMenuPermission(sysUser));
                    user.setUserRole(roles.iterator().next());
                    setUserAgent(user);
                    user.setSuQian(true);
                    RedisUtils.setCacheObject(userKey, user, 120, TimeUnit.MINUTES);
                    return true;
                }
                // 查询四维用户
                GatewayUserDetail gatewayUserDetail = feignUserManageService.userDetail(id);
                log.info("四维用户详情：{}", gatewayUserDetail);
                // 如果非空则新增到数据库
                if (gatewayUserDetail.getCode() == 200 && gatewayUserDetail.getData() != null && StrUtil.isNotBlank(gatewayUserDetail.getData().getUserId())) {
                    GatewayUserDetail.UserDetail data = gatewayUserDetail.getData();
                    SysUser newUser = new SysUser();
                    newUser.setUserId(data.getUserId());
                    newUser.setUserName(data.getUserName());
                    newUser.setNickName(data.getName());
                    newUser.setUserType(Convert.toStr(data.getUserGroupId()));
                    newUser.setPhonenumber(data.getMobile());
                    newUser.setPassword(SecurityUtils.encryptPassword("tocc!suqian"));
                    newUser.setStatus("0");
                    newUser.setDelFlag("0");
                    newUser.setUpdateBy("gateway");
                    newUser.setUpdateTime(new Date());
                    newUser.setCreateBy("gateway");
                    newUser.setCreateTime(new Date());
                    userService.insertUser(newUser);
                    throw new ServiceException("请联系应急事件处置系统管理员分配组织及权限");
                } else {
                    throw new ServiceException("该用户不存在，请重新登录");
                }
            }
            return true;
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
        throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
        throws Exception {

    }

    private void setUserAgent(LoginUser loginUser) {
        UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
        String ip = ServletUtils.getClientIP();
        loginUser.setIpaddr(ip);
        loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
        loginUser.setBrowser(userAgent.getBrowser().getName());
        loginUser.setOs(userAgent.getOs().getName());
    }

}
