package com.mosquito.project.permission;

import com.mosquito.project.permission.entity.SysUserEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import static com.mosquito.project.permission.RoleConstants.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 权限判断服务 - 核心权限验证逻辑
 * 支持PRD四段式与冒号格式兼容
 */
@Service
public class PermissionCheckService {

    private static final Logger log = LoggerFactory.getLogger(PermissionCheckService.class);

    private final RoleRepository roleRepository;
    private final PermissionRepository permissionRepository;
    private final UserRoleRepository userRoleRepository;
    private final RolePermissionRepository rolePermissionRepository;
    private final PermissionCodeResolver permissionCodeResolver;
    private final DepartmentRepository departmentRepository;
    private final SysUserService sysUserService;

    public PermissionCheckService(RoleRepository roleRepository,
                                  PermissionRepository permissionRepository,
                                  UserRoleRepository userRoleRepository,
                                  RolePermissionRepository rolePermissionRepository,
                                  PermissionCodeResolver permissionCodeResolver,
                                  DepartmentRepository departmentRepository,
                                  SysUserService sysUserService) {
        this.roleRepository = roleRepository;
        this.permissionRepository = permissionRepository;
        this.userRoleRepository = userRoleRepository;
        this.rolePermissionRepository = rolePermissionRepository;
        this.permissionCodeResolver = permissionCodeResolver;
        this.departmentRepository = departmentRepository;
        this.sysUserService = sysUserService;
    }

    /**
     * 检查用户是否拥有指定权限
     * 支持PRD四段式和冒号格式权限码
     * 权限来源：1) 角色权限 2) 用户直接分配的权限
     */
    public boolean hasPermission(Long userId, String permissionCode) {
        // 解析权限码（支持别名映射）
        String resolvedCode = permissionCodeResolver.resolve(permissionCode);

        // 1. 检查用户直接权限
        if (hasDirectPermission(userId, permissionCode, resolvedCode)) {
            return true;
        }

        // 2. 检查角色权限
        Set<String> userRoles = getUserRoleCodes(userId);
        if (userRoles.isEmpty()) {
            return false;
        }

        return userRoles.stream()
                .anyMatch(roleCode -> roleHasPermission(roleCode, permissionCode, resolvedCode));
    }

    /**
     * 检查用户是否拥有直接权限
     */
    private boolean hasDirectPermission(Long userId, String permissionCode, String resolvedCode) {
        try {
            List<Long> directPermissionIds = sysUserService.getUserDirectPermissionIds(userId);
            if (directPermissionIds.isEmpty()) {
                return false;
            }

            // 将权限码转换为ID进行比对
            List<Long> matchedIds = permissionRepository.findByPermissionCodeIn(List.of(permissionCode, resolvedCode))
                    .stream()
                    .map(p -> p.getId())
                    .collect(Collectors.toList());

            for (Long pid : matchedIds) {
                if (directPermissionIds.contains(pid)) {
                    return true;
                }
            }
        } catch (Exception e) {
            log.warn("检查用户直接权限失败: userId={}, error={}", userId, e.getMessage());
        }
        return false;
    }

    /**
     * 检查用户是否拥有指定角色
     */
    public boolean hasRole(Long userId, String roleCode) {
        Set<String> userRoles = getUserRoleCodes(userId);
        return userRoles.contains(roleCode);
    }

    /**
     * 获取用户所有角色代码列表
     */
    public List<String> getUserRoles(Long userId) {
        return new ArrayList<>(getUserRoleCodes(userId));
    }

    /**
     * 获取用户所有权限代码
     */
    public Set<String> getUserPermissions(Long userId) {
        Set<String> userRoles = getUserRoleCodes(userId);
        if (userRoles.isEmpty()) {
            return Set.of();
        }

        // 获取所有角色对应的权限
        return userRoles.stream()
                .flatMap(roleCode -> getRolePermissions(roleCode).stream())
                .collect(Collectors.toSet());
    }

    /**
     * 获取用户数据权限范围
     * 根据用户角色返回数据权限：ALL（全部）、DEPARTMENT（部门）、OWN（个人）
     */
    public String getUserDataScope(Long userId) {
        Set<String> userRoles = getUserRoleCodes(userId);
        if (userRoles.isEmpty()) {
            return "OWN"; // 默认个人权限
        }

        // 遍历用户角色，返回最高级别数据权限
        for (String role : userRoles) {
            if (RoleConstants.hasAllDataScope(role)) {
                return "ALL";
            }
        }
        for (String role : userRoles) {
            if (RoleConstants.hasDepartmentDataScope(role)) {
                return "DEPARTMENT";
            }
        }
        return "OWN";
    }

    /**
     * 判断用户是否有查看所有数据的权限
     */
    public boolean canViewAllData(Long userId) {
        return "ALL".equals(getUserDataScope(userId));
    }

    /**
     * 判断用户是否可以查看部门数据
     */
    public boolean canViewDepartmentData(Long userId) {
        String scope = getUserDataScope(userId);
        return "ALL".equals(scope) || "DEPARTMENT".equals(scope);
    }

    /**
     * 判断用户是否只能查看个人数据
     */
    public boolean canViewOnlyOwnData(Long userId) {
        return "OWN".equals(getUserDataScope(userId));
    }

    /**
     * 检查用户是否有权访问指定部门的数据
     * @param userId 当前用户ID
     * @param departmentId 数据所属部门ID（用户所在部门）
     * @param ownerId 数据所属用户ID（创建者）
     * @param currentUserDepartmentId 当前用户所在部门ID
     * @return 是否有权访问
     */
    public boolean canAccessData(Long userId, Long departmentId, Long ownerId, Long currentUserDepartmentId) {
        String scope = getUserDataScope(userId);

        switch (scope) {
            case "ALL":
                return true; // 全部数据权限
            case "DEPARTMENT":
                // 部门数据权限：只能访问本部门及下级部门的数据
                List<Long> deptIds = getUserDepartmentAndChildIds(userId);
                return departmentId != null && deptIds.contains(departmentId);
            case "OWN":
            default:
                // 个人数据权限：只能访问自己的数据
                return ownerId != null && ownerId.equals(userId);
        }
    }

    /**
     * 获取用户所在部门ID
     * @param userId 用户ID
     * @return 部门ID，如果没有部门则返回null
     */
    public Long getUserDepartmentId(Long userId) {
        Optional<SysUserEntity> userOpt = sysUserService.getUserById(userId);
        return userOpt.map(SysUserEntity::getDepartmentId).orElse(null);
    }

    /**
     * 获取用户所在部门及所有下级部门ID列表
     * @param userId 用户ID
     * @return 部门ID列表（包含本部门及下级部门）
     */
    public List<Long> getUserDepartmentAndChildIds(Long userId) {
        Long deptId = getUserDepartmentId(userId);
        if (deptId == null) {
            return List.of();
        }

        // 获取本部门及所有下级部门ID
        List<Long> allDeptIds = new ArrayList<>();
        allDeptIds.add(deptId);

        try {
            List<Long> descendantIds = departmentRepository.findAllDescendantDeptIds(deptId);
            if (descendantIds != null && !descendantIds.isEmpty()) {
                allDeptIds.addAll(descendantIds);
            }
        } catch (Exception e) {
            // 如果递归查询失败，至少返回本部门ID
            log.warn("获取下级部门失败，仅返回本部门ID: {}", deptId);
        }

        return allDeptIds;
    }

    /**
     * 获取用户角色代码列表
     */
    private Set<String> getUserRoleCodes(Long userId) {
        // 1. 获取用户所有角色ID
        List<Long> roleIds = userRoleRepository.findRoleIdsByUserId(userId);
        if (roleIds.isEmpty()) {
            return Set.of();
        }

        // 2. 根据角色ID获取角色代码
        return roleIds.stream()
                .map(roleId -> roleRepository.findById(roleId))
                .filter(Optional::isPresent)
                .map(Optional::get)
                .map(SysRole::getRoleCode)
                .collect(Collectors.toSet());
    }

    /**
     * 检查角色是否拥有指定权限
     * 支持原始权限码和规范权限码的匹配
     * 支持通配符匹配（如 activity.list.view.* 匹配 activity.list.view.ALL）
     * 支持无scope权限码与带.ALL权限码的互相匹配
     */
    private boolean roleHasPermission(String roleCode, String originalCode, String resolvedCode) {
        Set<String> permissions = getRolePermissions(roleCode);

        // 1. 精确匹配原始权限码
        if (permissions.contains(originalCode)) {
            return true;
        }

        // 2. 精确匹配解析后的规范权限码
        if (resolvedCode != null && permissions.contains(resolvedCode)) {
            return true;
        }

        // 3. 无scope与带.ALL的兼容匹配
        // 如 DB存 dashboard.chart.realtime，代码用 dashboard.chart.realtime.ALL
        if (matchesScopeLessWithAllScope(permissions, originalCode)) {
            return true;
        }
        if (resolvedCode != null && matchesScopeLessWithAllScope(permissions, resolvedCode)) {
            return true;
        }

        // 4. 带.ALL与无scope的兼容匹配
        // 如 DB存 dashboard.chart.realtime.ALL，代码用 dashboard.chart.realtime
        if (matchesAllScopeWithScopeLess(permissions, originalCode)) {
            return true;
        }
        if (resolvedCode != null && matchesAllScopeWithScopeLess(permissions, resolvedCode)) {
            return true;
        }

        // 5. 通配符匹配
        if (matchWithWildcard(permissions, originalCode)) {
            return true;
        }
        if (resolvedCode != null && matchWithWildcard(permissions, resolvedCode)) {
            return true;
        }

        return false;
    }

    /**
     * 检查权限集合中是否存在与目标权限码兼容的无scope权限码
     * 如目标: dashboard.chart.realtime.ALL，DB中: dashboard.chart.realtime
     */
    private boolean matchesScopeLessWithAllScope(Set<String> permissions, String targetCode) {
        if (targetCode == null) return false;
        String scopeLess = permissionCodeResolver.toScopeLessFormat(targetCode);
        if (scopeLess != null && !scopeLess.equals(targetCode)) {
            return permissions.contains(scopeLess);
        }
        return false;
    }

    /**
     * 检查权限集合中是否存在与目标权限码兼容的带ALL权限码
     * 如目标: dashboard.chart.realtime，DB中: dashboard.chart.realtime.ALL
     */
    private boolean matchesAllScopeWithScopeLess(Set<String> permissions, String targetCode) {
        if (targetCode == null) return false;
        String withAll = permissionCodeResolver.toAllScopeFormat(targetCode);
        if (withAll != null && !withAll.equals(targetCode)) {
            return permissions.contains(withAll);
        }
        return false;
    }

    /**
     * 通配符匹配权限码
     * 支持 * 匹配任意一段
     * 如 activity.list.view.* 可匹配 activity.list.view.ALL/DEPARTMENT/OWN
     */
    private boolean matchWithWildcard(Set<String> permissions, String targetCode) {
        if (targetCode == null || !targetCode.contains("*")) {
            return false;
        }

        for (String permission : permissions) {
            if (matchesWildcard(permission, targetCode)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断permission是否匹配pattern（支持通配符）
     * pattern中的*匹配任意一段（非贪婪）
     */
    private boolean matchesWildcard(String permission, String pattern) {
        String[] permParts = permission.split("\\.");
        String[] patternParts = pattern.split("\\.");

        if (permParts.length != patternParts.length) {
            return false;
        }

        for (int i = 0; i < permParts.length; i++) {
            if (!"*".equals(patternParts[i]) && !patternParts[i].equalsIgnoreCase(permParts[i])) {
                return false;
            }
        }
        return true;
    }

    /**
     * 获取角色的所有权限
     */
    private Set<String> getRolePermissions(String roleCode) {
        // 1. 根据角色代码获取角色ID
        List<Long> roleIds = rolePermissionRepository.findRoleIdsByRoleCode(roleCode);
        if (roleIds.isEmpty()) {
            return Set.of();
        }

        // 2. 获取角色所有权限ID
        Long roleId = roleIds.get(0);
        List<Long> permissionIds = rolePermissionRepository.findPermissionIdsByRoleId(roleId);
        if (permissionIds.isEmpty()) {
            return Set.of();
        }

        // 3. 根据权限ID获取权限代码
        return permissionIds.stream()
                .map(permId -> permissionRepository.findById(permId))
                .filter(Optional::isPresent)
                .map(Optional::get)
                .map(SysPermission::getPermissionCode)
                .collect(Collectors.toSet());
    }
}
