package com.microservices.common.security.utils;

import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.constant.SecurityConstants;
import com.microservices.common.core.constant.TokenConstants;
import com.microservices.common.core.context.SecurityContextHolder;
import com.microservices.common.core.exception.ServiceException;
import com.microservices.common.core.utils.ServletUtils;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.security.annotation.RequiresPermissions;
import com.microservices.system.api.domain.SysRole;
import com.microservices.system.api.domain.SysUser;
import com.microservices.system.api.domain.SysUserDeptRole;
import com.microservices.system.api.model.LoginUser;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 权限获取工具类
 *
 * @author microservices
 */
public class SecurityUtils
{
    /**
     * 获取用户ID
     */
    public static Long getUserId()
    {
        return SecurityContextHolder.getUserId();
    }

    /**
     * 获取组织ID
     */
    public static Long getDeptId() {
        return SecurityContextHolder.getDeptId();
    }

    /**
     * 获取用户名称
     */
    public static String getUsername()
    {
        return SecurityContextHolder.getUserName();
    }

    /**
     * 获取登录GitlinkId
     */
    public static Long getGitlinkUserId() {
        LoginUser loginUser = getLoginUser();
        if (loginUser == null) {
            return null;
        }
        SysUser sysUser = loginUser.getSysUser();
        if (sysUser == null) {
            return null;
        }
        return sysUser.getGitlinkUserId();
    }

    /**
     * 获取用户key
     */
    public static String getUserKey()
    {
        return SecurityContextHolder.getUserKey();
    }

    /**
     * 获取登录用户信息
     */
    public static LoginUser getLoginUser() {
        return SecurityContextHolder.get(SecurityConstants.LOGIN_USER, LoginUser.class);
    }

    /**
     * 获取登录用户身份
     */
    public static SysUserDeptRole getUserIdentity() {
        return SecurityContextHolder.get(SecurityConstants.USER_IDENTITY, SysUserDeptRole.class);
    }

    /**
     * 设置忽略数据权限校验
     */
    public static void setIgnoreDataScopeCheck() {
        SecurityContextHolder.set(SecurityConstants.IGNORE_DATA_SCOPE_CHECK, true);
    }

    /**
     * 是否忽略数据权限校验
     */
    public static Boolean getIgnoreDataScopeCheck() {
        Boolean ignoreCheck = SecurityContextHolder.get(SecurityConstants.IGNORE_DATA_SCOPE_CHECK, Boolean.class);
        if (ignoreCheck == null) {
            return false;
        }
        return ignoreCheck;
    }

    /**
     * 获取登录用户身份
     */
    public static Boolean checkCurrentDeptId(Long deptId) {
        Boolean ignoreDataScopeCheck = getIgnoreDataScopeCheck();
        if (ignoreDataScopeCheck) {
            return true;
        }
        SysUserDeptRole userIdentify = getUserIdentity();
        if (userIdentify == null) {
            return false;
        }
        return Objects.equals(userIdentify.getDeptId(), deptId);
    }

    /**
     * 获取角色键值
     */
    public static String getRoleKey() {
        SysUserDeptRole userIdentity = getUserIdentity();
        if (userIdentity != null) {
            SysRole sysRole = userIdentity.getSysRole();
            if (sysRole != null) {
                return sysRole.getRoleKey();
            }
        }
        return null;
    }

    /**
     * 获取请求token
     */
    public static String getToken() {
        return getToken(ServletUtils.getRequest());
    }

    /**
     * 根据request获取请求token
     */
    public static String getToken(HttpServletRequest request)
    {
        if (request == null) {
            return null;
        }
        // 从header获取token标识
        String token = request.getHeader(TokenConstants.AUTHENTICATION);
        return replaceTokenPrefix(token);
    }

    /**
     * 裁剪token前缀
     */
    public static String replaceTokenPrefix(String token)
    {
        // 如果前端设置了令牌前缀，则裁剪掉前缀
        if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX))
        {
            token = token.replaceFirst(TokenConstants.PREFIX, "");
        }
        return token;
    }

    /**
     * 是否为管理员
     *
     * @param userId 用户ID
     * @return 结果
     */
    public static boolean isAdmin(Long userId) {
        return userId != null && 1L == userId;
    }

    /**
     * 判断当前登录用户是否为管理员
     *
     * @return 结果
     */
    public static boolean isAdmin() {
        LoginUser loginUser = getLoginUser();
        Long userId = loginUser.getUserId();
        return userId != null && 1L == userId;
    }

    /**
     * 生成BCryptPasswordEncoder密码
     *
     * @param password 密码
     * @return 加密字符串
     */
    public static String encryptPassword(String password) {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        return passwordEncoder.encode(password);
    }

    /**
     * 判断密码是否相同
     *
     * @param rawPassword 真实密码
     * @param encodedPassword 加密后字符
     * @return 结果
     */
    public static boolean matchesPassword(String rawPassword, String encodedPassword)
    {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        return passwordEncoder.matches(rawPassword, encodedPassword);
    }


    /**
     * 获取微服务下所有权限清单
     *
     * @param basePackage 包路径（示例：com.microservices.pms.*.controller）
     * @return 权限清单
     */
    public static List<JSONObject> getAllPermissions(String basePackage) {
        List<JSONObject> jsonObjectList = new ArrayList<>();
        ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
        provider.addIncludeFilter(new AnnotationTypeFilter(RestController.class));

        Set<BeanDefinition> candidateComponents = new HashSet<>();
        for (String packageToScan : basePackage.split(",")) {
            candidateComponents.addAll(provider.findCandidateComponents(packageToScan));
        }

        try {
            for (BeanDefinition candidateComponent : candidateComponents) {
                JSONObject controllerObject = new JSONObject();
                String basePath = "";
                Class<?> clazz = Class.forName(candidateComponent.getBeanClassName());
                String controllerName = clazz.getName();
                if (clazz.isAnnotationPresent(Api.class)) {
                    Api api = clazz.getAnnotation(Api.class);
                    String[] tags = api.tags();
                    if (StringUtils.isNotEmpty(tags) && !api.hidden()) {
                        controllerName = String.join(",", tags);
                    }
                }
                if (clazz.isAnnotationPresent(RequestMapping.class)) {
                    RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
                    if (requestMapping.value().length > 0) {
                        basePath = requestMapping.value()[0];
                    }
                }

                controllerObject.put("Controller", controllerName);
                List<JSONObject> methodList = new ArrayList<>();
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    StringBuilder path = new StringBuilder(basePath);
                    if (method.isAnnotationPresent(RequiresPermissions.class)) {
                        String methodName = method.getName();
                        String requestMethodName = "";
                        if (method.isAnnotationPresent(ApiOperation.class)) {
                            ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
                            methodName = apiOperation.value();
                        }

                        if (method.isAnnotationPresent(GetMapping.class)) {
                            GetMapping requestMapping = method.getAnnotation(GetMapping.class);
                            if (requestMapping.value().length > 0) {
                                path.append(requestMapping.value()[0]);
                            }
                            requestMethodName = "GET ";
                        }

                        if (method.isAnnotationPresent(PostMapping.class)) {
                            PostMapping requestMapping = method.getAnnotation(PostMapping.class);
                            if (requestMapping.value().length > 0) {
                                path.append(requestMapping.value()[0]);
                            }
                            requestMethodName = "POST ";
                        }

                        if (method.isAnnotationPresent(DeleteMapping.class)) {
                            DeleteMapping requestMapping = method.getAnnotation(DeleteMapping.class);
                            if (requestMapping.value().length > 0) {
                                path.append(requestMapping.value()[0]);
                            }
                            requestMethodName = "DELETE ";
                        }

                        if (method.isAnnotationPresent(PutMapping.class)) {
                            PutMapping requestMapping = method.getAnnotation(PutMapping.class);
                            if (requestMapping.value().length > 0) {
                                path.append(requestMapping.value()[0]);
                            }
                            requestMethodName = "PUT ";
                        }

                        RequiresPermissions requiresPermissions = method.getAnnotation(RequiresPermissions.class);
                        String[] permissionsValue = requiresPermissions.value();
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put("methodName", methodName);
                        jsonObject.put("permissionsValue", String.join(",", permissionsValue));
                        jsonObject.put("url", String.format("%s%s", requestMethodName, path));
                        methodList.add(jsonObject);
                    }
                }
                controllerObject.put("methodList", methodList);
                jsonObjectList.add(controllerObject);
            }
        } catch (Exception e) {
            throw new ServiceException("错误错误：%s", e.getMessage());
        }
        return jsonObjectList;
    }
}
