package cn.novalon.manage.sys.core.service.impl;

import cn.novalon.manage.common.util.StatusConstants;
import cn.novalon.manage.sys.core.domain.SysUser;
import cn.novalon.manage.sys.core.domain.SysRole;
import cn.novalon.manage.sys.core.domain.UserRole;
import cn.novalon.manage.common.dto.PageRequest;
import cn.novalon.manage.common.dto.PageResponse;
import cn.novalon.manage.sys.core.repository.ISysUserRepository;
import cn.novalon.manage.sys.core.repository.ISysRoleRepository;
import cn.novalon.manage.sys.core.repository.IUserRoleRepository;
import cn.novalon.manage.sys.core.service.ISysUserService;
import cn.novalon.manage.sys.core.command.CreateUserCommand;
import cn.novalon.manage.sys.core.command.UpdateUserCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;
import java.util.List;

/**
 * 用户服务实现类
 * 
 * 文件定义：实现用户管理的核心业务逻辑
 * 涉及业务：用户注册、登录、信息修改、删除、密码修改、逻辑删除等用户生命周期管理
 * 算法：使用R2DBC进行响应式数据库操作，支持分页查询、条件查询、批量操作
 * 
 * @author 张翔
 * @date 2026-03-13
 */
@Service
public class SysUserService implements ISysUserService {

    private static final Logger logger = LoggerFactory.getLogger(SysUserService.class);
    private final ISysUserRepository userRepository;
    private final ISysRoleRepository roleRepository;
    private final IUserRoleRepository userRoleRepository;
    private final PasswordEncoder passwordEncoder;

    public SysUserService(ISysUserRepository userRepository, 
                         ISysRoleRepository roleRepository,
                         IUserRoleRepository userRoleRepository,
                         @Qualifier("passwordEncoder") PasswordEncoder passwordEncoder) {
        this.userRepository = userRepository;
        this.roleRepository = roleRepository;
        this.userRoleRepository = userRoleRepository;
        this.passwordEncoder = passwordEncoder;
        
        logger.info("使用的密码编码器类型: {}", passwordEncoder.getClass().getName());
    }

    @SuppressWarnings("unused")
    private static final BCryptPasswordEncoder directEncoder = new BCryptPasswordEncoder(12);

    @Override
    public Mono<SysUser> findById(Long id) {
        return userRepository.findById(id);
    }

    @Override
    public Flux<SysUser> findAll() {
        return userRepository.findAll();
    }

    @Override
    public Flux<SysUser> findAll(boolean includeDeleted) {
        if (includeDeleted) {
            return userRepository.findAll();
        } else {
            return userRepository.findByDeletedAtIsNull();
        }
    }

    @Override
    public Mono<PageResponse<SysUser>> findUsersByPage(PageRequest pageRequest) {
        return userRepository.findByQueryWithPagination(null, pageRequest);
    }

    @Override
    public Mono<Long> count() {
        return userRepository.count();
    }

    @Override
    public Mono<SysUser> findByUsername(String username) {
        return userRepository.findByUsername(username);
    }

    @Override
    public Mono<SysUser> createUser(SysUser user) {
        logger.info("SysUserService.createUser - 用户名: {}, 密码前缀: {}",
                user.getUsername(),
                user.getPassword() != null ? user.getPassword().substring(0, 7) : "null");
        if (user.getPassword() != null && !user.getPassword().startsWith("$2a$")
                && !user.getPassword().startsWith("$2b$")) {
            logger.info("密码不以$2a$或$2b$开头，重新编码");
            user.setPassword(passwordEncoder.encode(user.getPassword()));
            logger.info("重新编码后的密码前缀: {}", user.getPassword().substring(0, 7));
        } else {
            logger.info("密码已编码，跳过重新编码");
        }
        user.setCreatedAt(LocalDateTime.now());
        if (user.getStatus() == null) {
            user.setStatus(StatusConstants.ENABLED);
        }
        return userRepository.save(user);
    }

    @Override
    public Mono<SysUser> createUser(CreateUserCommand command) {
        SysUser user = new SysUser();
        user.setUsername(command.username().getValue());
        user.setPassword(passwordEncoder.encode(command.password().getValue()));
        user.setEmail(command.email().getValue());
        user.setNickname(command.nickname());
        user.setPhone(command.phone());
        user.setRoleId(command.roleId());
        user.setStatus(command.status() != null ? command.status() : StatusConstants.ENABLED);
        user.setCreatedAt(LocalDateTime.now());
        return userRepository.save(user);
    }

    @Override
    public Mono<SysUser> updateUser(SysUser user) {
        user.setUpdatedAt(LocalDateTime.now());
        return userRepository.save(user);
    }

    @Override
    public Mono<SysUser> updateUser(UpdateUserCommand command) {
        return userRepository.findById(command.id())
                .switchIfEmpty(Mono.error(new RuntimeException("User not found")))
                .flatMap(user -> {
                    if (command.username() != null) {
                        user.setUsername(command.username());
                    }
                    if (command.password() != null) {
                        user.setPassword(passwordEncoder.encode(command.password()));
                    }
                    if (command.email() != null) {
                        user.setEmail(command.email());
                    }
                    if (command.clearRole()) {
                        user.setRoleId(null);
                    } else if (command.roleId() != null) {
                        user.setRoleId(command.roleId());
                    }
                    if (command.status() != null) {
                        user.setStatus(command.status());
                    }
                    user.setUpdatedAt(LocalDateTime.now());
                    return userRepository.save(user);
                });
    }

    @Override
    @Transactional
    public Mono<Void> deleteUser(Long id) {
        logger.debug("开始删除用户，ID: {}", id);
        
        return userRepository.findById(id)
                .switchIfEmpty(Mono.error(new RuntimeException("User not found")))
                .flatMap(user -> {
                    logger.debug("找到用户，开始删除关联记录");
                    return userRoleRepository.deleteByUserId(id)
                            .doOnSuccess(v -> logger.debug("成功删除用户角色关联记录"))
                            .doOnError(e -> logger.error("删除用户角色关联记录失败", e))
                            .then(userRepository.deleteById(id))
                            .doOnSuccess(v -> logger.debug("成功删除用户"))
                            .doOnError(e -> logger.error("删除用户失败", e));
                });
    }

    @Override
    public Mono<Void> updateRoleIdToNullByRoleId(Long roleId) {
        return userRepository.updateRoleIdToNullByRoleId(roleId);
    }

    @Override
    public Mono<SysUser> changePassword(Long userId, String oldPassword, String newPassword) {
        return userRepository.findById(userId)
                .flatMap(user -> {
                    if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
                        return Mono.error(new RuntimeException("旧密码不正确"));
                    }
                    user.setPassword(passwordEncoder.encode(newPassword));
                    user.setUpdatedAt(LocalDateTime.now());
                    return userRepository.save(user);
                });
    }

    @Override
    public Mono<Boolean> existsByUsername(String username) {
        return userRepository.findByUsername(username)
                .map(user -> user != null)
                .defaultIfEmpty(false);
    }

    @Override
    public Mono<Boolean> existsByEmail(String email) {
        return userRepository.findByEmail(email)
                .map(user -> user != null)
                .defaultIfEmpty(false);
    }

    @Override
    public Mono<Void> logicalDeleteUser(Long id) {
        return userRepository.findByIdIncludingDeleted(id)
                .flatMap(user -> {
                    user.setDeletedAt(LocalDateTime.now());
                    return userRepository.save(user);
                })
                .then();
    }

    @Override
    public Mono<Void> logicalDeleteUsers(List<Long> ids) {
        return userRepository.logicalDeleteByIds(ids);
    }

    @Override
    public Mono<Void> restoreUser(Long id) {
        return userRepository.findByIdIncludingDeleted(id)
                .flatMap(user -> {
                    user.setDeletedAt(null);
                    return userRepository.save(user);
                })
                .then();
    }

    @Override
    public Mono<Void> restoreUsers(List<Long> ids) {
        return userRepository.restoreByIds(ids);
    }

    @Override
    @Transactional
    public Mono<Void> assignRolesToUser(Long userId, List<Long> roleIds) {
        logger.debug("开始为用户分配角色，用户ID: {}, 角色IDs: {}", userId, roleIds);
        
        if (roleIds == null || roleIds.isEmpty()) {
            logger.debug("角色列表为空，删除用户的所有角色关联");
            return userRoleRepository.deleteByUserId(userId)
                    .doOnSuccess(v -> logger.debug("成功删除用户的所有角色关联"))
                    .doOnError(e -> logger.error("删除用户角色关联失败", e));
        }
        
        return userRoleRepository.deleteByUserId(userId)
                .doOnSuccess(v -> logger.debug("成功删除用户的旧角色关联"))
                .doOnError(e -> logger.error("删除用户旧角色关联失败", e))
                .then(
                    Flux.fromIterable(roleIds)
                        .concatMap(roleId -> {
                            logger.debug("为用户分配角色ID: {}", roleId);
                            UserRole userRole = new UserRole();
                            userRole.setUserId(userId);
                            userRole.setRoleId(roleId);
                            userRole.setCreatedAt(LocalDateTime.now());
                            return userRoleRepository.save(userRole)
                                    .doOnSuccess(v -> logger.debug("成功保存用户角色关联"))
                                    .doOnError(e -> logger.error("保存用户角色关联失败", e));
                        })
                        .then()
                );
    }

    @Override
    public Flux<SysRole> getUserRoles(Long userId) {
        return userRoleRepository.findByUserId(userId)
                .flatMap(userRole -> roleRepository.findById(userRole.getRoleId()));
    }

    @Override
    public Flux<Long> getUserRoleIds(Long userId) {
        return userRoleRepository.findByUserId(userId)
                .map(UserRole::getRoleId);
    }
}
