AuditController.java

package com.edtech.controller;

import com.edtech.dto.AuditLogDto;
import com.edtech.model.AuditLog;
import com.edtech.model.User;
import com.edtech.repository.AuditLogRepository;
import com.edtech.repository.AuditLogSpecification;
import com.edtech.repository.UserRepository;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/** Controller para buscar logs de auditoria. */
@RestController
@RequestMapping("/api/audit-logs")
public class AuditController {

  private final AuditLogRepository auditLogRepository;
  private final UserRepository userRepository;

  /**
   * Construtor.
   *
   * @param auditLogRepository repo
   * @param userRepository repo
   */
  public AuditController(AuditLogRepository auditLogRepository, UserRepository userRepository) {
    this.auditLogRepository = auditLogRepository;
    this.userRepository = userRepository;
  }

  /**
   * Busca logs de auditoria com filtro.
   *
   * @param search texto de busca
   * @param action acao filtrada
   * @return lista de logs
   */
  @GetMapping
  @PreAuthorize("hasRole('AUDITOR')")
  public ResponseEntity<Page<AuditLogDto>> getAuditLogs(
      @RequestParam(required = false) String search,
      @RequestParam(required = false) String action,
      @RequestParam(required = false) String startDate,
      @RequestParam(required = false) String endDate,
      @PageableDefault(sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable) {

    LocalDateTime start = null;
    LocalDateTime end = null;
    try {
      if (startDate != null && !startDate.isEmpty()) {
        start = LocalDateTime.parse(startDate);
      }
      if (endDate != null && !endDate.isEmpty()) {
        end = LocalDateTime.parse(endDate);
      }
    } catch (DateTimeParseException ignored) {
      // ignored
    }

    Page<AuditLog> logsPage =
        auditLogRepository.findAll(
            AuditLogSpecification.getFilter(search, action, start, end), pageable);
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

    java.util.Set<UUID> userIds = logsPage.getContent().stream()
        .map(AuditLog::getUserId)
        .collect(java.util.stream.Collectors.toSet());
    java.util.Map<UUID, User> userMap = userRepository.findAllById(userIds).stream()
        .collect(java.util.stream.Collectors.toMap(User::getId, u -> u));

    List<AuditLogDto> dtos =
        logsPage.getContent().stream()
            .map(
                log -> {
                  AuditLogDto dto = new AuditLogDto();
                  dto.setId(log.getId().toString());
                  dto.setTimestamp(log.getCreatedAt().format(formatter));
                  dto.setAction(log.getAction().name());

                  User user = userMap.get(log.getUserId());
                  dto.setUserName(user != null ? user.getName() : "Unknown");

                  dto.setIp(log.getIpAddress());
                  dto.setDetails(log.getDetails());

                  switch (log.getAction()) {
                    case LOGIN_SUCCESS:
                    case DOCUMENT_APPROVED:
                      dto.setActionClass("green");
                      dto.setSeverity("INFO");
                      break;
                    case LOGIN_FAILED:
                    case DOCUMENT_REJECTED:
                    case MEMBER_JOINED:
                      dto.setActionClass("orange");
                      dto.setSeverity("WARNING");
                      break;
                    case DELETE_DOCUMENT:
                      dto.setActionClass("red");
                      dto.setSeverity("CRITICAL");
                      break;
                    default:
                      dto.setActionClass("blue");
                      dto.setSeverity("INFO");
                  }
                  return dto;
                })
            .collect(Collectors.toList());

    return ResponseEntity.ok(new PageImpl<>(dtos, pageable, logsPage.getTotalElements()));
  }

  /**
   * Exporta logs de auditoria para CSV.
   *
   * @param search texto de busca
   * @param action acao filtrada
   * @return Arquivo CSV
   */
  @GetMapping("/export")
  @PreAuthorize("hasRole('AUDITOR')")
  public ResponseEntity<String> exportAuditLogs(
      @RequestParam(required = false) String search,
      @RequestParam(required = false) String action,
      @RequestParam(required = false) String startDate,
      @RequestParam(required = false) String endDate) {

    ResponseEntity<Page<AuditLogDto>> response =
        getAuditLogs(
            search,
            action,
            startDate,
            endDate,
            org.springframework.data.domain.PageRequest.of(
                0, 10000, Sort.by(Sort.Direction.DESC, "createdAt")));
    List<AuditLogDto> dtos = response.getBody().getContent();

    StringBuilder csv = new StringBuilder();
    csv.append("ID,Timestamp,Action,User,IP,Details,Severity\n");

    if (dtos != null) {
      for (AuditLogDto dto : dtos) {
        csv.append(escapeCsv(dto.getId()))
            .append(",")
            .append(escapeCsv(dto.getTimestamp()))
            .append(",")
            .append(escapeCsv(dto.getAction()))
            .append(",")
            .append(escapeCsv(dto.getUserName()))
            .append(",")
            .append(escapeCsv(dto.getIp()))
            .append(",")
            .append(escapeCsv(dto.getDetails()))
            .append(",")
            .append(escapeCsv(dto.getSeverity()))
            .append("\n");
      }
    }

    org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();
    headers.add("Content-Disposition", "attachment; filename=\"audit_logs.csv\"");
    headers.add("Content-Type", "text/csv; charset=UTF-8");

    return ResponseEntity.ok().headers(headers).body(csv.toString());
  }

  private String escapeCsv(String value) {
    if (value == null) {
      return "";
    }
    String escaped = value.replace("\"", "\"\"");
    if (escaped.contains(",") || escaped.contains("\"") || escaped.contains("\n")) {
      return "\"" + escaped + "\"";
    }
    return escaped;
  }
}