ProjectService.java

package com.edtech.service;

import com.edtech.dto.ProjectMemberRequestDto;
import com.edtech.dto.ProjectRequestDto;
import com.edtech.dto.ProjectResponseDto;
import com.edtech.model.AuditAction;
import com.edtech.model.Project;
import com.edtech.model.ProjectMember;
import com.edtech.model.ProjectRole;
import com.edtech.model.User;
import com.edtech.repository.ProjectMemberRepository;
import com.edtech.repository.ProjectRepository;
import com.edtech.repository.UserRepository;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/** Documentação para ProjectService. */
@Service
public class ProjectService {

  private final ProjectRepository projectRepository;
  private final ProjectMemberRepository projectMemberRepository;
  private final UserRepository userRepository;
  private final AuditLogService auditLogService;

  /** Documentação. */
  public ProjectService(
      ProjectRepository projectRepository,
      ProjectMemberRepository projectMemberRepository,
      UserRepository userRepository,
      AuditLogService auditLogService) {
    this.projectRepository = projectRepository;
    this.projectMemberRepository = projectMemberRepository;
    this.userRepository = userRepository;
    this.auditLogService = auditLogService;
  }

  /** Documentação. */
  @Transactional
  @CacheEvict(value = "projects", key = "#advisorId")
  public ProjectResponseDto createProject(ProjectRequestDto request, UUID advisorId) {
    User advisor =
        userRepository
            .findById(advisorId)
            .orElseThrow(() -> new RuntimeException("User not found"));

    Project project = new Project();
    project.setTitle(request.getTitle());
    project.setDescription(request.getDescription());
    project.setAdvisor(advisor);

    project = projectRepository.save(project);

    ProjectMember member = new ProjectMember();
    member.setProject(project);
    member.setUser(advisor);
    member.setRole(ProjectRole.ADVISOR);
    projectMemberRepository.save(member);

    return mapToDto(project);
  }

  /** Documentação para o método listProjectsByUser. */
  @Cacheable(value = "projects", key = "#userId")
  public List<ProjectResponseDto> listProjectsByUser(UUID userId) {
    return projectRepository.findProjectsByUserId(userId).stream()
        .map(this::mapToDto)
        .collect(Collectors.toList());
  }

  /** Documentação. */
  @Transactional
  @CacheEvict(value = "projects", allEntries = true)
  public void addMember(UUID projectId, ProjectMemberRequestDto dto, User authenticatedUser) {
    Project project =
        projectRepository
            .findById(projectId)
            .orElseThrow(() -> new RuntimeException("Project not found"));

    UUID targetUserId =
        (dto != null && dto.getUserId() != null) ? dto.getUserId() : authenticatedUser.getId();

    // Se o usuário que está tentando associar alguém for diferente do alvo, ele deve ser ADVISOR do
    // projeto.
    if (!targetUserId.equals(authenticatedUser.getId())) {
      ProjectMember advisorMember =
          projectMemberRepository
              .findByProjectIdAndUserId(projectId, authenticatedUser.getId())
              .orElseThrow(
                  () -> new RuntimeException("User is not part of the project to add members"));

      if (advisorMember.getRole() != ProjectRole.ADVISOR) {
        throw new RuntimeException("Only ADVISORS can add other members");
      }
    }

    if (projectMemberRepository.findByProjectIdAndUserId(projectId, targetUserId).isPresent()) {
      throw new RuntimeException("User is already a member of this project");
    }

    User newUser =
        userRepository
            .findById(targetUserId)
            .orElseThrow(() -> new RuntimeException("User not found"));

    String roleStr =
        (dto != null && dto.getRole() != null) ? dto.getRole().toUpperCase() : "RESEARCHER";

    ProjectMember newMember = new ProjectMember();
    newMember.setProject(project);
    newMember.setUser(newUser);
    newMember.setRole(ProjectRole.valueOf(roleStr));
    projectMemberRepository.save(newMember);

    auditLogService.logAction(
        authenticatedUser.getId(),
        AuditAction.MEMBER_JOINED,
        "User " + newUser.getEmail() + " joined project " + project.getTitle());
  }

  private ProjectResponseDto mapToDto(Project project) {
    ProjectResponseDto dto = new ProjectResponseDto();
    dto.setId(project.getId());
    dto.setTitle(project.getTitle());
    dto.setDescription(project.getDescription());
    dto.setAdvisorId(project.getAdvisor().getId());
    dto.setCreatedAt(project.getCreatedAt());
    return dto;
  }
}