package com.github.starhq.app.service.impl;

import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.dao.DuplicateKeyException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.starhq.app.config.spring.security.auth.SecurityContextUtils;
import com.github.starhq.app.dto.security.RoleAuthority;
import com.github.starhq.app.dto.user.ResetPasswordDto;
import com.github.starhq.app.dto.user.UserDto;
import com.github.starhq.app.entity.Role;
import com.github.starhq.app.entity.User;
import com.github.starhq.app.entity.UserRole;
import com.github.starhq.app.entity.enums.UserStatus;
import com.github.starhq.app.exception.BusinessException;
import com.github.starhq.app.exception.DuplicateException;
import com.github.starhq.app.mappper.RoleMapper;
import com.github.starhq.app.mappper.UserMapper;
import com.github.starhq.app.mappper.UserRoleMapper;
import com.github.starhq.app.service.AuthService;

import lombok.RequiredArgsConstructor;

@Service("authService")
@RequiredArgsConstructor
public class AuthServiceImpl implements AuthService {

    private final UserMapper userMapper;
    private final RoleMapper roleMapper;
    private final UserRoleMapper userRoleMapper;
    private final PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        User user = userMapper.selectOne(queryWrapper);
        if (Objects.isNull(user)) {
            throw new BadCredentialsException("error.user.login.fail");
        }

        if (!Objects.equals(UserStatus.ACTIVE, user.getStatus())) {
            throw new DisabledException("error.user.login.disabled");
        }

        List<Role> roles = roleMapper.selectAssignedRolesByUserId(user.getId());
        if (CollectionUtils.isEmpty(roles)) {
            throw new AccessDeniedException("error.user.login.no_roles");
        }
        Set<RoleAuthority> authorities = roles.stream().map(role -> new RoleAuthority(role.getId(), role.getCode()))
                .collect(Collectors.toSet());

        user.setAuthorities(authorities);
        return user;
    }

    @Transactional
    @Override
    public UserDetails register(UserDto userDto) {
        User user = new User();
        user.setUsername(userDto.getUsername());
        user.setPassword(passwordEncoder.encode(userDto.getPassword()));
        user.setStatus(UserStatus.ACTIVE);
        try {
            if (userMapper.insert(user) <= 0) {
                throw new BusinessException("err.user.register.failed");
            }
        } catch (DuplicateKeyException e) {
            throw new DuplicateException("error.duplicate.username", new Object[] { user.getUsername() }, e);
        }

        Role defaultRole = roleMapper.getDefaultRole();
        if (Objects.isNull(defaultRole)) {
            throw new BusinessException("err.user.register.no_default_role");
        }

        UserRole userRole = new UserRole();
        userRole.setUserId(user.getId());
        userRole.setRoleId(defaultRole.getId());
        if (userRoleMapper.insert(userRole) <= 0) {
            throw new RuntimeException("err.user.register.no_default_role");
        }
        RoleAuthority authority = new RoleAuthority(defaultRole.getId(), defaultRole.getCode());
        user.setAuthorities(Set.of(authority));

        return user;
    }

    @Override
    public boolean resetPassword(ResetPasswordDto resetPasswordDto) {
        Long userId = SecurityContextUtils.getCurrentUserId();
        User user = userMapper.selectById(userId);
        if (Objects.isNull(user)) {
            throw new BadCredentialsException("error.user.login.fail");
        }
        String oldPassword = resetPasswordDto.getOldPassword();
        String newPassword = resetPasswordDto.getNewPassword();
        if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
            throw new BadCredentialsException("error.user.login.fail");
        }
        user.setPassword(passwordEncoder.encode(newPassword));
        return userMapper.updateById(user) > 0;
    }

}
Please optimize the code and add comments