RegistroAuditoriaController.java

package com.licensis.notaire.api;

import com.licensis.notaire.dto.DtoRegistroAuditoria;
import com.licensis.notaire.negocio.RegistroAuditoria;
import com.licensis.notaire.service.RegistroAuditoriaService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.transaction.annotation.Transactional;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/api/v1/registro-auditoria")
@Tag(name = "RegistroAuditoria", description = "API para consultar y administrar auditoria de usuarios")
public class RegistroAuditoriaController {

    private final RegistroAuditoriaService service;

    public RegistroAuditoriaController(RegistroAuditoriaService service) {
        this.service = service;
    }

    /**
     * Returns audit log entries. Supports pagination, sort and optional
     * filters by {@code modulo} and {@code idUsuario}. When no pagination
     * parameters are present the response is a flat list of DTOs (kept for
     * backwards compatibility with the existing Swing/Next clients).
     */
    @GetMapping
    @Operation(summary = "Obtener los registros de auditoria con paginación y filtros opcionales")
    @Transactional(readOnly = true)
    public ResponseEntity<?> getAll(
            @RequestParam(required = false) Integer page,
            @RequestParam(required = false) Integer size,
            @RequestParam(required = false, defaultValue = "fecha,desc") String sort,
            @RequestParam(required = false) String modulo,
            @RequestParam(required = false) Integer idUsuario
    ) {
        boolean paginated = page != null || size != null;
        if (!paginated) {
            List<DtoRegistroAuditoria> dtos = service.findAllAsDto();
            return ResponseEntity.ok(dtos);
        }

        Pageable pageable = buildPageable(page, size, sort);
        Page<DtoRegistroAuditoria> result;
        if (modulo != null && !modulo.isBlank()) {
            result = service.findByModuloAsDto(modulo, pageable);
        } else if (idUsuario != null) {
            result = service.findByUsuarioIdAsDto(idUsuario, pageable);
        } else {
            result = service.findAllAsDto(pageable);
        }
        return ResponseEntity.ok(toPageResponse(result));
    }

    @ApiResponses({
    @ApiResponse(responseCode = "200", description = "OK"),
    @ApiResponse(responseCode = "404", description = "No encontrado")
})
    @GetMapping("/{id}")
    @Operation(summary = "Obtener registro de auditoria por ID")
    @Transactional(readOnly = true)
    public ResponseEntity<DtoRegistroAuditoria> getById(@PathVariable Integer id) {
        return service.findByIdAsDto(id)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }

    @GetMapping("/usuario/{idUsuario}")
    @Operation(summary = "Obtener registros de auditoria por usuario")
    @Transactional(readOnly = true)
    public ResponseEntity<List<DtoRegistroAuditoria>> getByUsuario(@PathVariable Integer idUsuario) {
        return ResponseEntity.ok(service.findByUsuarioIdAsDto(idUsuario));
    }

    @ApiResponses({
    @ApiResponse(responseCode = "201", description = "Creado"),
    @ApiResponse(responseCode = "400", description = "Solicitud inválida"),
    @ApiResponse(responseCode = "409", description = "Conflicto")
})
    @PostMapping
    @Operation(summary = "Crear nuevo registro de auditoria")
    public ResponseEntity<RegistroAuditoria> create(@RequestBody RegistroAuditoria entity) {
        RegistroAuditoria saved = service.save(entity);
        return ResponseEntity.ok(saved);
    }

    private Pageable buildPageable(Integer page, Integer size, String sort) {
        int safePage = page == null || page < 0 ? 0 : page;
        int safeSize = size == null || size <= 0 ? 20 : size;
        Sort sortSpec = parseSort(sort);
        return PageRequest.of(safePage, safeSize, sortSpec);
    }

    private Sort parseSort(String sort) {
        if (sort == null || sort.isBlank()) {
            return Sort.by(Sort.Direction.DESC, "fecha");
        }
        String[] parts = sort.split(",");
        String property = parts[0].trim();
        Sort.Direction direction = Sort.Direction.DESC;
        if (parts.length > 1) {
            try {
                direction = Sort.Direction.fromString(parts[1].trim());
            } catch (IllegalArgumentException ex) {
                direction = Sort.Direction.DESC;
            }
        }
        return Sort.by(direction, property);
    }

    private Map<String, Object> toPageResponse(Page<DtoRegistroAuditoria> page) {
        Map<String, Object> response = new LinkedHashMap<>();
        response.put("content", page.getContent());
        response.put("page", page.getNumber());
        response.put("size", page.getSize());
        response.put("totalElements", page.getTotalElements());
        response.put("totalPages", page.getTotalPages());
        response.put("first", page.isFirst());
        response.put("last", page.isLast());
        return response;
    }
}