package com.imprimelibros.erp.auth;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.util.Base64;
import java.util.Locale;
import java.util.Map;

import org.springframework.context.MessageSource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring6.SpringTemplateEngine;

import com.imprimelibros.erp.common.email.EmailService;
import com.imprimelibros.erp.users.User;
import com.imprimelibros.erp.users.UserDao; // ajusta al path real de tu UserRepository

@Service
public class PasswordResetService {

    private final PasswordResetTokenRepository tokenRepo;
    private final UserDao userRepo;
    private final PasswordEncoder passwordEncoder;
    private final JavaMailSender mailSender;
    private final SpringTemplateEngine templateEngine;
    private final MessageSource messages;
    private final EmailService emailService;

    public PasswordResetService(
        PasswordResetTokenRepository tokenRepo,
        UserDao userRepo,
        PasswordEncoder passwordEncoder,
        JavaMailSender mailSender,
        SpringTemplateEngine templateEngine,
        MessageSource messages,
        EmailService emailService
    ) {
        this.tokenRepo = tokenRepo;
        this.userRepo = userRepo;
        this.passwordEncoder = passwordEncoder;
        this.mailSender = mailSender;
        this.templateEngine = templateEngine;
        this.messages = messages;
        this.emailService = emailService;
    }

    // 3.1 Solicitar reset (si el email existe, genera token y envía)
    @Transactional
    public void requestReset(String email, String baseUrl, String ip, String userAgent, int minutes, Locale locale) {
        
        User user = userRepo.findByUserNameIgnoreCase(email).orElse(null);
        // Siempre responder OK aunque no exista para evitar enumeración
        if (user == null) return;

        tokenRepo.invalidateActiveTokens(user.getId(), LocalDateTime.now());

        String token = generateToken();                 // token en claro SOLO para el enlace
        String tokenHash = sha256(token);               // guardamos hash en DB

        PasswordResetToken row = new PasswordResetToken();
        row.setUserId(user.getId());
        row.setCreatedAt(LocalDateTime.now());
        row.setExpiresAt(LocalDateTime.now().plusMinutes(minutes));
        row.setRequestIp(ip);
        row.setUserAgent(userAgent);
        row.setTokenHash(tokenHash);
        tokenRepo.save(row);

        String resetUrl = baseUrl + "/auth/password/reset?uid=" + user.getId() + "&token=" + token;

        emailService.sendPasswordResetMail(user.getUserName(), user.getFullName(), resetUrl, locale);
    }

    // 3.2 Validar token (para mostrar el formulario de nueva contraseña)
    public boolean isValid(Long userId, String token) {
        String hash = sha256(token);
        return tokenRepo.findValidByUserAndHash(userId, hash, LocalDateTime.now()).isPresent();
    }

    // 3.3 Confirmar reseteo y marcar token como usado
    @Transactional
    public boolean resetPassword(Long userId, String token, String newPassword) {
        String hash = sha256(token);
        var opt = tokenRepo.findValidByUserAndHash(userId, hash, LocalDateTime.now());
        if (opt.isEmpty()) return false;

        var entry = opt.get();
        var user = userRepo.findById(userId).orElse(null);
        if (user == null) return false;

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

        entry.setUsedAt(LocalDateTime.now());
        tokenRepo.save(entry);

        // (Opcional) invalidar otros tokens activos del usuario
        tokenRepo.invalidateActiveTokens(userId, LocalDateTime.now());
        return true;
    }

    // --- helpers ---
    private String generateToken() {
        byte[] buf = new byte[32];
        new SecureRandom().nextBytes(buf);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(buf);
    }
    private String sha256(String s) {
        try {
            var md = MessageDigest.getInstance("SHA-256");
            return Base64.getEncoder().encodeToString(md.digest(s.getBytes(StandardCharsets.UTF_8)));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    
}

