package com.mosquito.project.permission;

import com.mosquito.project.permission.entity.SysUserEntity;
import com.mosquito.project.permission.entity.SysUserPermissionEntity;
import com.mosquito.project.permission.entity.SysUserTokenEntity;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * 系统用户服务
 */
@Service
public class SysUserService {

    private final UserRepository userRepository;
    private final UserTokenRepository userTokenRepository;
    private final RoleRepository roleRepository;
    private final UserRoleRepository userRoleRepository;
    private final DepartmentRepository departmentRepository;
    private final PermissionRepository permissionRepository;
    private final UserPermissionRepository userPermissionRepository;
    private final PermissionAuditService auditService;

    public SysUserService(UserRepository userRepository,
                         UserTokenRepository userTokenRepository,
                         RoleRepository roleRepository,
                         UserRoleRepository userRoleRepository,
                         DepartmentRepository departmentRepository,
                         PermissionRepository permissionRepository,
                         UserPermissionRepository userPermissionRepository,
                         PermissionAuditService auditService) {
        this.userRepository = userRepository;
        this.userTokenRepository = userTokenRepository;
        this.roleRepository = roleRepository;
        this.userRoleRepository = userRoleRepository;
        this.departmentRepository = departmentRepository;
        this.permissionRepository = permissionRepository;
        this.userPermissionRepository = userPermissionRepository;
        this.auditService = auditService;
    }

    /**
     * 获取用户数据权限范围
     */
    private 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";
    }

    /**
     * 获取用户角色代码列表
     */
    private Set<String> getUserRoleCodes(Long userId) {
        List<Long> roleIds = userRoleRepository.findRoleIdsByUserId(userId);
        if (roleIds.isEmpty()) {
            return Set.of();
        }
        return roleIds.stream()
                .map(roleId -> roleRepository.findById(roleId))
                .filter(Optional::isPresent)
                .map(Optional::get)
                .map(role -> role.getRoleCode())
                .collect(Collectors.toSet());
    }

    /**
     * 获取用户所在部门及所有下级部门ID列表
     */
    private List<Long> getUserDepartmentAndChildIds(Long userId) {
        Optional<SysUserEntity> userOpt = userRepository.findById(userId);
        Long deptId = userOpt.map(SysUserEntity::getDepartmentId).orElse(null);
        if (deptId == null) {
            return List.of();
        }

        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
        }

        return allDeptIds;
    }

    /**
     * 分页查询用户列表（带数据权限过滤）
     */
    public Page<SysUserEntity> listUsers(int page, int size, String status, String keyword, Long currentUserId) {
        Pageable pageable = PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "createdAt"));

        // 处理null和空字符串
        String statusParam = (status != null && !status.isEmpty()) ? status : null;
        String keywordParam = (keyword != null && !keyword.isEmpty()) ? keyword : null;

        // 数据权限过滤
        if (currentUserId != null) {
            String dataScope = getUserDataScope(currentUserId);
            if ("ALL".equals(dataScope)) {
                // 全部数据权限，不过滤
                return userRepository.searchUsers(statusParam, keywordParam, pageable);
            } else if ("DEPARTMENT".equals(dataScope)) {
                // 部门数据权限
                List<Long> deptIds = getUserDepartmentAndChildIds(currentUserId);
                return userRepository.searchUsersWithDataScope(statusParam, keywordParam, null, deptIds, pageable);
            } else {
                // 个人数据权限 (OWN)
                return userRepository.searchUsersWithDataScope(statusParam, keywordParam, currentUserId, null, pageable);
            }
        }

        // 无用户ID时返回全部（保守策略）
        return userRepository.searchUsers(statusParam, keywordParam, pageable);
    }

    /**
     * 根据ID获取用户
     */
    public Optional<SysUserEntity> getUserById(Long userId) {
        return userRepository.findById(userId);
    }

    /**
     * 根据用户名获取用户
     */
    public Optional<SysUserEntity> getUserByUsername(String username) {
        return userRepository.findByUsername(username);
    }

    /**
     * 创建用户
     */
    @Transactional
    public SysUserEntity createUser(SysUserEntity user) {
        if (userRepository.existsByUsername(user.getUsername())) {
            throw new IllegalArgumentException("用户名已存在");
        }
        user.setCreatedAt(LocalDateTime.now());
        user.setUpdatedAt(LocalDateTime.now());
        return userRepository.save(user);
    }

    /**
     * 更新用户
     */
    @Transactional
    public Optional<SysUserEntity> updateUser(Long userId, SysUserEntity user) {
        return userRepository.findById(userId).map(existing -> {
            if (user.getRealName() != null) existing.setRealName(user.getRealName());
            if (user.getEmail() != null) existing.setEmail(user.getEmail());
            if (user.getPhone() != null) existing.setPhone(user.getPhone());
            if (user.getDepartmentId() != null) existing.setDepartmentId(user.getDepartmentId());
            if (user.getStatus() != null) existing.setStatus(user.getStatus());
            if (user.getIsCertified() != null) existing.setIsCertified(user.getIsCertified());
            existing.setUpdatedAt(LocalDateTime.now());
            return userRepository.save(existing);
        });
    }

    /**
     * 专用密码哈希更新方法，避免通过通用资料更新接口丢失密码变更
     */
    @Transactional
    public void updatePasswordHash(Long userId, String passwordHash) {
        if (passwordHash == null || passwordHash.isBlank()) {
            throw new IllegalArgumentException("密码哈希不能为空");
        }

        SysUserEntity user = userRepository.findById(userId)
                .orElseThrow(() -> new IllegalArgumentException("用户不存在: " + userId));
        user.setPasswordHash(passwordHash);
        user.setUpdatedAt(LocalDateTime.now());
        userRepository.save(user);
    }

    /**
     * 删除用户（软删除）
     */
    @Transactional
    public void deleteUser(Long userId) {
        userRepository.findById(userId).ifPresent(user -> {
            user.setStatus("DELETED");
            user.setUpdatedAt(LocalDateTime.now());
            userRepository.save(user);
        });
    }

    /**
     * 冻结用户
     */
    @Transactional
    public boolean freezeUser(Long userId) {
        return userRepository.findById(userId).map(user -> {
            user.setStatus("FROZEN");
            user.setUpdatedAt(LocalDateTime.now());
            userRepository.save(user);
            // 使该用户的所有token失效
            userTokenRepository.deleteExpiredOrByUserId(LocalDateTime.now(), userId);
            return true;
        }).orElse(false);
    }

    /**
     * 解冻用户
     */
    @Transactional
    public boolean unfreezeUser(Long userId) {
        return userRepository.findById(userId).map(user -> {
            user.setStatus("ACTIVE");
            user.setUpdatedAt(LocalDateTime.now());
            userRepository.save(user);
            return true;
        }).orElse(false);
    }

    /**
     * 实名认证
     */
    @Transactional
    public boolean certifyUser(Long userId) {
        return userRepository.findById(userId).map(user -> {
            user.setIsCertified(true);
            user.setUpdatedAt(LocalDateTime.now());
            userRepository.save(user);
            return true;
        }).orElse(false);
    }

    /**
     * 添加到黑名单
     */
    @Transactional
    public boolean addToBlacklist(Long userId) {
        return addToBlacklist(userId, null);
    }

    /**
     * 添加到黑名单（带原因）
     */
    @Transactional
    public boolean addToBlacklist(Long userId, String reason) {
        return userRepository.findById(userId).map(user -> {
            user.setIsBlacklisted(true);
            user.setUpdatedAt(LocalDateTime.now());
            userRepository.save(user);
            // 使该用户的所有token失效
            userTokenRepository.deleteExpiredOrByUserId(LocalDateTime.now(), userId);
            return true;
        }).orElse(false);
    }

    /**
     * 封禁用户（永久封禁）
     * 相当于添加到黑名单+冻结
     */
    @Transactional
    public boolean banUser(Long userId, String reason) {
        // 先冻结账号
        boolean frozen = freezeUser(userId);
        // 再添加到黑名单
        boolean blacklisted = addToBlacklist(userId, reason);
        return frozen && blacklisted;
    }

    /**
     * 从黑名单移除
     */
    @Transactional
    public boolean removeFromBlacklist(Long userId) {
        return userRepository.findById(userId).map(user -> {
            user.setIsBlacklisted(false);
            user.setUpdatedAt(LocalDateTime.now());
            userRepository.save(user);
            return true;
        }).orElse(false);
    }

    /**
     * 获取黑名单列表（分页）
     */
    public Page<SysUserEntity> getBlacklist(Integer page, Integer size, String keyword) {
        int pageNum = page != null && page > 0 ? page - 1 : 0;
        int pageSize = size != null && size > 0 ? size : 10;
        Pageable pageable = PageRequest.of(pageNum, pageSize, Sort.by(Sort.Direction.DESC, "updatedAt"));

        if (keyword != null && !keyword.isBlank()) {
            return userRepository.searchBlacklist(keyword, pageable);
        }
        return userRepository.findByIsBlacklisted(true, pageable);
    }

    /**
     * 创建Token
     */
    @Transactional
    public SysUserTokenEntity createToken(Long userId, String ipAddress, String userAgent, int expireHours) {
        // 删除该用户的旧token
        userTokenRepository.deleteExpiredOrByUserId(LocalDateTime.now(), userId);

        SysUserTokenEntity token = new SysUserTokenEntity();
        token.setUserId(userId);
        token.setToken(UUID.randomUUID().toString().replace("-", ""));
        token.setExpiresAt(LocalDateTime.now().plusHours(expireHours));
        token.setIpAddress(ipAddress);
        token.setUserAgent(userAgent);
        token.setCreatedAt(LocalDateTime.now());

        // 更新用户最后登录时间
        userRepository.findById(userId).ifPresent(user -> {
            user.setLastLoginAt(LocalDateTime.now());
            userRepository.save(user);
        });

        return userTokenRepository.save(token);
    }

    /**
     * 验证Token
     */
    public Optional<SysUserTokenEntity> verifyToken(String token) {
        return userTokenRepository.findByToken(token)
                .filter(t -> t.getExpiresAt().isAfter(LocalDateTime.now()));
    }

    /**
     * 删除Token（登出）
     */
    @Transactional
    public void deleteToken(String token) {
        userTokenRepository.findByToken(token).ifPresent(userTokenRepository::delete);
    }

    /**
     * 清理过期Token
     */
    @Transactional
    public void cleanupExpiredTokens() {
        userTokenRepository.deleteExpiredTokens(LocalDateTime.now());
    }

    /**
     * 获取用户权限列表（通过角色）
     */
    public List<SysPermission> getUserPermissions(Long userId) {
        Set<String> roleCodes = getUserRoleCodes(userId);
        if (roleCodes.isEmpty()) {
            return List.of();
        }

        // 获取用户所有角色对应的权限
        List<Long> roleIds = userRoleRepository.findRoleIdsByUserId(userId);
        if (roleIds.isEmpty()) {
            return List.of();
        }

        return permissionRepository.findByRoleIds(roleIds);
    }

    /**
     * 为用户分配直接权限（不通过角色）
     * @param userId 用户ID
     * @param permissionIds 权限ID列表
     * @param grantedBy 授权人ID（当前操作的管理员）
     */
    @Transactional
    public void assignPermissions(Long userId, List<Long> permissionIds, Long grantedBy) {
        if (userId == null || permissionIds == null || permissionIds.isEmpty()) {
            throw new IllegalArgumentException("参数不能为空");
        }

        // 验证用户存在
        if (!userRepository.existsById(userId)) {
            throw new IllegalArgumentException("用户不存在: " + userId);
        }

        // 验证权限存在
        for (Long permissionId : permissionIds) {
            if (!permissionRepository.existsById(permissionId)) {
                throw new IllegalArgumentException("权限不存在: " + permissionId);
            }

            // 如果用户尚未拥有该权限，则添加
            if (!userPermissionRepository.existsByUserIdAndPermissionId(userId, permissionId)) {
                SysUserPermissionEntity userPermission = new SysUserPermissionEntity(userId, permissionId, grantedBy);
                userPermissionRepository.save(userPermission);

                // 记录权限分配审计日志
                if (auditService != null) {
                    permissionRepository.findById(permissionId).ifPresent(perm ->
                        auditService.logAssignPermission(grantedBy, userId, permissionId,
                            perm.getPermissionCode(), null, "用户权限分配")
                    );
                }
            }
        }
    }

    /**
     * 兼容旧接口（不带grantedBy参数）
     */
    @Transactional
    public void assignPermissions(Long userId, List<Long> permissionIds) {
        assignPermissions(userId, permissionIds, null);
    }

    /**
     * 撤销用户的直接权限
     * @param userId 用户ID
     * @param permissionIds 权限ID列表
     */
    @Transactional
    public void revokePermissions(Long userId, List<Long> permissionIds) {
        if (userId == null || permissionIds == null || permissionIds.isEmpty()) {
            throw new IllegalArgumentException("参数不能为空");
        }

        for (Long permissionId : permissionIds) {
            // 记录撤销前的权限信息用于审计
            if (auditService != null) {
                permissionRepository.findById(permissionId).ifPresent(perm ->
                    auditService.logRevokePermission(null, userId, permissionId,
                        perm.getPermissionCode(), null, "用户权限撤销")
                );
            }
            userPermissionRepository.deleteByUserIdAndPermissionId(userId, permissionId);
        }
    }

    /**
     * 获取用户直接拥有的权限ID列表
     */
    public List<Long> getUserDirectPermissionIds(Long userId) {
        return userPermissionRepository.findPermissionIdsByUserId(userId);
    }

    /**
     * 更新用户数据权限范围
     * @param userId 用户ID
     * @param dataScope 数据权限范围 (ALL/DEPARTMENT/OWN)
     * @param departmentId 部门ID（DEPARTMENT范围时使用）
     */
    @Transactional
    public void updateDataScope(Long userId, String dataScope, Long departmentId) {
        SysUserEntity user = userRepository.findById(userId)
                .orElseThrow(() -> new IllegalArgumentException("用户不存在: " + userId));

        // 记录旧值用于审计
        String oldDataScope = user.getDataScope();

        // 验证数据权限范围
        if (!"ALL".equals(dataScope) && !"DEPARTMENT".equals(dataScope) && !"OWN".equals(dataScope)) {
            throw new IllegalArgumentException("无效的数据权限范围: " + dataScope);
        }

        // 如果是DEPARTMENT，验证部门ID
        if ("DEPARTMENT".equals(dataScope) && departmentId != null) {
            if (!departmentRepository.existsById(departmentId)) {
                throw new IllegalArgumentException("部门不存在: " + departmentId);
            }
        }

        // 真正保存数据权限配置
        user.setDataScope(dataScope);
        if ("DEPARTMENT".equals(dataScope)) {
            user.setDepartmentId(departmentId);
        }
        user.setUpdatedAt(LocalDateTime.now());
        userRepository.save(user);

        // 记录数据权限变更审计日志
        if (auditService != null && oldDataScope != null && !oldDataScope.equals(dataScope)) {
            auditService.logUpdateDataScope(null, userId, oldDataScope, dataScope,
                departmentId, null, "数据权限范围变更");
        }
    }

    /**
     * 获取用户数据权限范围
     */
    public String getUserDataScopeValue(Long userId) {
        return userRepository.findById(userId)
                .map(SysUserEntity::getDataScope)
                .orElse("OWN");
    }

    /**
     * 获取用户角色代码列表
     */
    public Set<String> getUserRoleCodesSet(Long userId) {
        return getUserRoleCodes(userId);
    }

    /**
     * 添加到白名单
     */
    @Transactional
    public boolean addToWhitelist(Long userId) {
        return userRepository.findById(userId).map(user -> {
            user.setIsWhitelisted(true);
            user.setUpdatedAt(LocalDateTime.now());
            userRepository.save(user);
            return true;
        }).orElse(false);
    }

    /**
     * 从白名单移除
     */
    @Transactional
    public boolean removeFromWhitelist(Long userId) {
        return userRepository.findById(userId).map(user -> {
            user.setIsWhitelisted(false);
            user.setUpdatedAt(LocalDateTime.now());
            userRepository.save(user);
            return true;
        }).orElse(false);
    }

    /**
     * 调整用户积分
     * @param userId 用户ID
     * @param amount 调整数量（正数为增加，负数为扣减）
     * @param reason 调整原因
     * @param operatorId 操作人ID
     * @return 新的积分余额
     */
    @Transactional
    public Long adjustPoints(Long userId, Long amount, String reason, Long operatorId) {
        SysUserEntity user = userRepository.findById(userId)
                .orElseThrow(() -> new IllegalArgumentException("用户不存在: " + userId));

        Long currentPoints = user.getPoints() != null ? user.getPoints() : 0L;
        Long newPoints = currentPoints + amount;

        if (newPoints < 0) {
            throw new IllegalArgumentException("积分不足，当前积分: " + currentPoints + "，扣减数量: " + Math.abs(amount));
        }

        user.setPoints(newPoints);
        user.setPointsUpdatedAt(LocalDateTime.now());
        user.setPointsUpdatedBy(operatorId);
        user.setUpdatedAt(LocalDateTime.now());
        userRepository.save(user);

        return newPoints;
    }

    /**
     * 获取用户当前积分
     */
    public Long getPoints(Long userId) {
        return userRepository.findById(userId)
                .map(user -> user.getPoints() != null ? user.getPoints() : 0L)
                .orElse(0L);
    }
}
