package com.njzscloud.common.security.permission;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.njzscloud.common.security.contant.Constants;
import com.njzscloud.common.security.contant.EndpointAccessModel;
import com.njzscloud.common.security.ex.ForbiddenAccessException;
import com.njzscloud.common.security.ex.MissingPermissionException;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;

import java.util.*;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 权限管理器
 */
@Slf4j
@RequiredArgsConstructor
public class PermissionManager {
    private static final int ACCESS_GRANTED = 1;

    private static final ReentrantLock PERMISSION_CACHE_LOCK = new ReentrantLock();
    /**
     * 权限缓存，请求地址——角色编码
     */
    private Map<PathPatternRequestMatcher, Collection<String>> PERMISSION_CACHE;

    private Set<PathPatternRequestMatcher> FORBIDDEN_CACHE;
    private static final int ACCESS_ABSTAIN = 0;
    private static final int ACCESS_DENIED = -1;
    private final PermissionLoader permissionLoader;

    public int vote(Authentication authentication, HttpServletRequest request) {
        Collection<String> attributes = extractAuthorities(request);
        if (authentication == null) {
            return ACCESS_DENIED;
        }
        int result = ACCESS_ABSTAIN;
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        for (String attribute : attributes) {
            result = ACCESS_DENIED;
            for (GrantedAuthority authority : authorities) {
                if (attribute.equals(authority.getAuthority())) {
                    return ACCESS_GRANTED;
                }
            }
        }
        return result;
    }

    /**
     * 刷新本地权限缓存
     */
    public final void refresh() {
        try {
            if (log.isDebugEnabled()) log.debug("刷新本地权限缓存已清除");
            PERMISSION_CACHE_LOCK.lock();
            PERMISSION_CACHE = null;
            FORBIDDEN_CACHE = null;
            if (log.isDebugEnabled()) log.debug("本地权限缓存已清除");
            this.load0();
        } finally {
            PERMISSION_CACHE_LOCK.unlock();
        }
    }

    /**
     * 初始化本地权限缓存
     */
    public final void init() {
        // if (log.isDebugEnabled()) log.debug("初始化本地权限缓存");
        if (CollUtil.isEmpty(PERMISSION_CACHE)) {
            try {
                PERMISSION_CACHE_LOCK.lock();
                if (CollUtil.isEmpty(PERMISSION_CACHE)) {
                    this.load0();
                }
            } finally {
                PERMISSION_CACHE_LOCK.unlock();
            }
        }
        // if (log.isDebugEnabled()) log.debug("已初始化无需操作");
    }

    /**
     * 加载权限
     */
    private void load0() {
        // if (log.isDebugEnabled()) log.debug("开始加载权限");

        List<RolePermission> rolePermissions = permissionLoader.load();
        if (rolePermissions == null) rolePermissions = new ArrayList<>();

        rolePermissions.add(RolePermission.DEFAULT);

        Map<PathPatternRequestMatcher, Collection<String>> permissionMap = new LinkedHashMap<>();

        Set<PathPatternRequestMatcher> forbiddenSet = new HashSet<>();

        for (RolePermission rolePermission : rolePermissions) {
            String endpoint = rolePermission.getEndpoint();
            String method = rolePermission.getMethod();
            EndpointAccessModel accessModel = rolePermission.getAccessModel();
            HttpMethod httpMethod = method == null ? null : HttpMethod.valueOf(method);
            PathPatternRequestMatcher pathRequestMatcher = PathPatternRequestMatcher.withDefaults().matcher(httpMethod, endpoint);
            if (accessModel == EndpointAccessModel.FORBIDDEN) {
                forbiddenSet.add(pathRequestMatcher);
                continue;
            }

            Collection<String> configAttributes = permissionMap.computeIfAbsent(pathRequestMatcher, it -> new HashSet<>());

            if (accessModel == EndpointAccessModel.ANONYMOUS) {
                configAttributes.add(Constants.ROLE_ANONYMOUS);
                configAttributes.add(Constants.ROLE_AUTHENTICATED);
            } else if (accessModel == EndpointAccessModel.LOGINED) {
                configAttributes.add(Constants.ROLE_AUTHENTICATED);
            } else if (accessModel == EndpointAccessModel.AUTHENTICATED) {
                String role = rolePermission.getRole();
                if (StrUtil.isNotBlank(role)) configAttributes.add(role);
                else configAttributes.add(Constants.ROLE_NONE);
            }
        }

        FORBIDDEN_CACHE = forbiddenSet;
        PERMISSION_CACHE = permissionMap;

        // if (log.isDebugEnabled()) log.debug("本地权限缓存已加载：\n{}", Jackson.toJsonStr(this.getAllRelation()));
    }

    /**
     * 获取当前请求所需要的角色
     *
     * @param request 请求对象
     * @return Collection&lt;String&gt;
     */
    public final Collection<String> extractAuthorities(HttpServletRequest request) {
        this.init();
        if (FORBIDDEN_CACHE != null) {
            for (PathPatternRequestMatcher PathPatternRequestMatcher : FORBIDDEN_CACHE) {
                if (PathPatternRequestMatcher.matches(request)) {
                    throw new ForbiddenAccessException("当前服务已停止使用！");
                }
            }
        }
        if (PERMISSION_CACHE != null) {
            for (Map.Entry<PathPatternRequestMatcher, Collection<String>> entry : PERMISSION_CACHE.entrySet()) {
                if (entry.getKey().matches(request)) {
                    return entry.getValue();
                }
            }
        }
        return CollUtil.empty(Set.class);
    }

    public Collection<String> getAttributes(HttpServletRequest request) {
        Collection<String> permission = this.extractAuthorities(request);
        String requestURI = request.getRequestURI();
        String method = request.getMethod();
        String endpoint = method.toUpperCase() + " " + requestURI;

        Assert.notEmpty(permission, () -> new MissingPermissionException(StrUtil.format("请求: 【{}】 未指定权限", endpoint)));
        return permission;
    }

    /**
     * 获取所有角色
     *
     * @return 角色列表
     */
    public final Collection<String> getAll() {
        this.init();
        Set<String> allAttributes = new HashSet<>();
        if (PERMISSION_CACHE != null) PERMISSION_CACHE.values().forEach(allAttributes::addAll);
        return CollUtil.unmodifiable(allAttributes);
    }

    /**
     * 获取权限的字符表示形式
     *
     * @return Map&lt;String, Set&lt;String&gt;&gt;
     */
    public synchronized final Map<String, Set<String>> getAllRelation() {
        Map<String, Set<String>> map = new HashMap<>();
        if (PERMISSION_CACHE != null) {
            Set<Map.Entry<PathPatternRequestMatcher, Collection<String>>> entries = PERMISSION_CACHE.entrySet();
            for (Map.Entry<PathPatternRequestMatcher, Collection<String>> entry : entries) {
                PathPatternRequestMatcher key = entry.getKey();
                Collection<String> value = entry.getValue();
                Set<String> collect = new HashSet<>(value);
                map.put(key.toString(), collect);
            }
        }
        return MapUtil.unmodifiable(map);
    }
}
