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

import cn.novalon.manage.common.util.StatusConstants;
import cn.novalon.manage.sys.core.domain.SysRole;
import cn.novalon.manage.sys.core.query.SysRoleQuery;
import cn.novalon.manage.sys.core.repository.ISysRoleRepository;
import cn.novalon.manage.sys.core.repository.IUserRoleRepository;
import cn.novalon.manage.sys.core.repository.ISysRolePermissionRepository;
import cn.novalon.manage.sys.core.service.ISysRoleService;
import cn.novalon.manage.sys.core.service.ISysUserService;
import cn.novalon.manage.sys.core.command.CreateRoleCommand;
import cn.novalon.manage.sys.core.command.UpdateRoleCommand;
import cn.novalon.manage.common.dto.PageRequest;
import cn.novalon.manage.common.dto.PageResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;

/**
 * 系统角色服务实现类
 * 
 * @author 张翔
 * @date 2026-03-14
 */
@Service
public class SysRoleService implements ISysRoleService {

    private static final Logger logger = LoggerFactory.getLogger(SysRoleService.class);
    private final ISysRoleRepository roleRepository;
    private final ISysUserService userService;
    private final IUserRoleRepository userRoleRepository;
    private final ISysRolePermissionRepository rolePermissionRepository;

    public SysRoleService(ISysRoleRepository roleRepository, ISysUserService userService,
                          IUserRoleRepository userRoleRepository, ISysRolePermissionRepository rolePermissionRepository) {
        this.roleRepository = roleRepository;
        this.userService = userService;
        this.userRoleRepository = userRoleRepository;
        this.rolePermissionRepository = rolePermissionRepository;
    }

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

    @Override
    public Flux<SysRole> findAll() {
        return roleRepository.findAll();
    }

    @Override
    public Mono<PageResponse<SysRole>> findRolesByPage(PageRequest pageRequest) {
        SysRoleQuery query = new SysRoleQuery();

        if (pageRequest.getKeyword() != null && !pageRequest.getKeyword().isEmpty()) {
            query.setKeyword(pageRequest.getKeyword());
        }

        return roleRepository.findByQueryWithPagination(query, pageRequest);
    }

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

    @Override
    public Mono<SysRole> createRole(SysRole role) {
        role.setCreatedAt(LocalDateTime.now());
        if (role.getStatus() == null) {
            role.setStatus(StatusConstants.ENABLED);
        }
        return roleRepository.save(role);
    }

    @Override
    public Mono<SysRole> createRole(CreateRoleCommand command) {
        SysRole role = new SysRole();
        role.setRoleName(command.roleName());
        role.setRoleKey(command.roleKey());
        role.setRoleSort(command.roleSort());
        role.setStatus(command.status() != null ? command.status() : StatusConstants.ENABLED);
        role.setCreatedAt(LocalDateTime.now());
        return roleRepository.save(role);
    }

    @Override
    public Mono<SysRole> updateRole(SysRole role) {
        role.setUpdatedAt(LocalDateTime.now());
        return roleRepository.save(role);
    }

    @Override
    public Mono<SysRole> updateRole(UpdateRoleCommand command) {
        return roleRepository.findById(command.id())
                .switchIfEmpty(Mono.error(new RuntimeException("Role not found")))
                .flatMap(role -> {
                    if (command.roleName() != null) {
                        role.setRoleName(command.roleName());
                    }
                    if (command.roleKey() != null) {
                        role.setRoleKey(command.roleKey());
                    }
                    if (command.roleSort() != null) {
                        role.setRoleSort(command.roleSort());
                    }
                    if (command.status() != null) {
                        role.setStatus(command.status());
                    }
                    role.setUpdatedAt(LocalDateTime.now());
                    return roleRepository.save(role);
                });
    }

    @Override
    @Transactional
    public Mono<Void> deleteRole(Long id) {
        logger.debug("开始删除角色，ID: {}", id);
        
        return roleRepository.findById(id)
                .flatMap(role -> {
                    logger.debug("找到角色，开始删除关联记录");
                    return userRoleRepository.deleteByRoleId(id)
                            .doOnSuccess(v -> logger.debug("成功删除用户角色关联记录"))
                            .doOnError(e -> logger.error("删除用户角色关联记录失败", e))
                            .then(rolePermissionRepository.deleteByRoleId(id))
                            .doOnSuccess(v -> logger.debug("成功删除角色权限关联记录"))
                            .doOnError(e -> logger.error("删除角色权限关联记录失败", e))
                            .then(userService.updateRoleIdToNullByRoleId(id))
                            .doOnSuccess(v -> logger.debug("成功更新用户角色ID为null"))
                            .doOnError(e -> logger.error("更新用户角色ID失败", e))
                            .then(roleRepository.deleteById(id))
                            .doOnSuccess(v -> logger.debug("成功删除角色"))
                            .doOnError(e -> logger.error("删除角色失败", e));
                });
    }

    @Override
    public Mono<SysRole> findByRoleName(String roleName) {
        return roleRepository.findByRoleName(roleName);
    }

    @Override
    public Mono<Boolean> existsByRoleName(String roleName) {
        return roleRepository.existsByRoleName(roleName);
    }

    @Override
    public Mono<SysRole> logicalDeleteRole(Long id) {
        return roleRepository.findByIdIncludingDeleted(id)
                .flatMap(role -> {
                    role.delete();
                    return roleRepository.updateRole(role);
                });
    }

    @Override
    public Mono<SysRole> restoreRole(Long id) {
        return roleRepository.findByIdIncludingDeleted(id)
                .flatMap(role -> {
                    role.restore();
                    return roleRepository.updateRole(role);
                });
    }
}
