package com.imprimelibros.erp.auth;

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import com.imprimelibros.erp.users.UserDao;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.HexFormat;

@Service
public class PasswordResetService {

    private final PasswordResetTokenRepository tokenRepo;
    private final UserDao userRepo; // tu repo real
    private final PasswordEncoder passwordEncoder;

    private static final SecureRandom RNG = new SecureRandom();
    private static final HexFormat HEX = HexFormat.of();

    public PasswordResetService(PasswordResetTokenRepository tokenRepo, UserDao userRepo, PasswordEncoder enc) {
        this.tokenRepo = tokenRepo;
        this.userRepo = userRepo;
        this.passwordEncoder = enc;
    }

    /** Elimina tokens previos sin usar, genera uno nuevo y lo guarda con auditoría básica. */
    public String createTokenForUser(Long userId, int hoursToExpire, String requestIp, String userAgent) {
        // Invalidar anteriores
        tokenRepo.deleteAllByUserIdAndUsedAtIsNull(userId);

        // Generar token
        byte[] raw = new byte[32];
        RNG.nextBytes(raw);
        String token = HEX.formatHex(raw);               // token plano (64 hex)
        String tokenHash = sha256(token);

        PasswordResetToken prt = new PasswordResetToken();
        prt.setUserId(userId);
        prt.setTokenHash(tokenHash);
        prt.setExpiresAt(Instant.now().plus(hoursToExpire, ChronoUnit.HOURS));
        prt.setRequestIp(truncate(requestIp, 64));
        prt.setUserAgent(truncate(userAgent, 255));
        tokenRepo.save(prt);

        return token; // Esto se envía por email
    }

    public Long validateTokenAndGetUserId(String tokenPlain) {
        return tokenRepo.findByTokenHashAndUsedAtIsNullAndExpiresAtAfter(sha256(tokenPlain), Instant.now())
                .map(PasswordResetToken::getUserId)
                .orElse(null);
    }

    public void consumeTokenAndSetPassword(String tokenPlain, String newPassword) {
        var tokenOpt = tokenRepo.findByTokenHashAndUsedAtIsNullAndExpiresAtAfter(sha256(tokenPlain), Instant.now());
        var prt = tokenOpt.orElseThrow(() -> new IllegalArgumentException("Token inválido o caducado"));

        var user = userRepo.findById(prt.getUserId())
                .orElseThrow(() -> new IllegalStateException("Usuario no encontrado"));

        user.setPassword(passwordEncoder.encode(newPassword));
        userRepo.save(user);

        prt.setUsedAt(Instant.now());
        tokenRepo.save(prt);
    }

    private static String sha256(String s) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            return HEX.formatHex(md.digest(s.getBytes(StandardCharsets.UTF_8)));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static String truncate(String v, int max) {
        if (v == null) return null;
        return v.length() <= max ? v : v.substring(0, max);
    }
}
