DataInitializer.java

package com.licensis.notaire.config;

import com.licensis.notaire.negocio.Persona;
import com.licensis.notaire.negocio.TipoIdentificacion;
import com.licensis.notaire.negocio.Usuario;
import com.licensis.notaire.repository.PersonaRepository;
import com.licensis.notaire.repository.TipoIdentificacionRepository;
import com.licensis.notaire.repository.UsuarioRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

/**
 * Siembra el usuario administrador inicial una única vez, si no existe ninguno.
 *
 * <p>Username y password son configurables vía {@code APP_ADMIN_USER}/
 * {@code APP_ADMIN_PASSWORD} (issue #651); por defecto {@code admin}/{@code admin}
 * en desarrollo. La contraseña se almacena como hash BCrypt (issue #554),
 * coincidiendo con la verificación que realiza {@code UsuarioController#login}.
 * Es un seed de una sola vez: si el usuario ya existe (sembrado por Flyway V2, o
 * porque un operador ya rotó su contraseña) no se toca — de lo contrario cualquier
 * cambio de credenciales quedaría deshecho en cada reinicio del backend (issue
 * #553).</p>
 */
@Component
public class DataInitializer implements ApplicationRunner {

    private static final Logger log = LoggerFactory.getLogger(DataInitializer.class);

    @Value("${app.admin.username:admin}")
    private String adminUsername;

    @Value("${app.admin.password:admin}")
    private String adminPassword;

    private final UsuarioRepository usuarioRepository;
    private final PersonaRepository personaRepository;
    private final TipoIdentificacionRepository tipoIdentificacionRepository;
    private final PasswordEncoder passwordEncoder;

    public DataInitializer(UsuarioRepository usuarioRepository,
                           PersonaRepository personaRepository,
                           TipoIdentificacionRepository tipoIdentificacionRepository,
                           PasswordEncoder passwordEncoder) {
        this.usuarioRepository = usuarioRepository;
        this.personaRepository = personaRepository;
        this.tipoIdentificacionRepository = tipoIdentificacionRepository;
        this.passwordEncoder = passwordEncoder;
    }

    @Override
    @Transactional
    public void run(ApplicationArguments args) {
        try {
            ensureAdminUser();
        } catch (Exception e) {
            log.warn("No se pudo garantizar el usuario inicial '{}': {}", adminUsername, e.getMessage());
        }
    }

    private void ensureAdminUser() {
        if (usuarioRepository.findByNombre(adminUsername).isPresent()) {
            log.debug("Usuario '{}' ya existe; no se modifican sus credenciales.", adminUsername);
            return;
        }

        log.info("Usuario '{}' no encontrado. Creando usuario administrador inicial...", adminUsername);
        Persona adminPersona = buildAdminPersona();
        personaRepository.save(adminPersona);

        Usuario admin = new Usuario();
        admin.setNombre(adminUsername);
        admin.setContrasenia(passwordEncoder.encode(adminPassword));
        admin.setEstado(true);
        admin.setTipo("Escribano");
        admin.setFkIdPersona(adminPersona);
        usuarioRepository.save(admin);
        log.info("Usuario administrador inicial '{}' creado correctamente.", adminUsername);
    }

    private Persona buildAdminPersona() {
        TipoIdentificacion tipo = tipoIdentificacionRepository.findAll().stream()
                .findFirst()
                .orElseGet(this::createDefaultTipoIdentificacion);

        Persona persona = new Persona();
        persona.setNombre("Admin");
        persona.setApellido("Sistema");
        persona.setEsCliente(false);
        persona.setNumeroIdentificacion("00000000");
        persona.setFkIdTipoIdentificacion(tipo);
        return persona;
    }

    private TipoIdentificacion createDefaultTipoIdentificacion() {
        TipoIdentificacion tipo = new TipoIdentificacion();
        tipo.setNombre("DNI");
        return tipoIdentificacionRepository.save(tipo);
    }

}