package com.imprimelibros.erp.auth;

import com.imprimelibros.erp.common.RateLimiterService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.constraints.NotBlank;

import org.springframework.context.MessageSource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import com.imprimelibros.erp.common.EmailService;
import com.imprimelibros.erp.users.UserDao;

import java.util.Locale;
import java.util.Map;

@Controller
@RequestMapping("/password")
@Validated
public class PasswordResetController {

    private final PasswordResetService resetService;
    private final EmailService emailService;          // tu servicio de correo HTML
    private final UserDao userRepo;           // tu repo de usuarios
    private final RateLimiterService rateLimiter;
    private final MessageSource messageSource;

    public PasswordResetController(PasswordResetService resetService,
                                   EmailService emailService,
                                   UserDao userRepo,
                                   RateLimiterService rateLimiter,
                                   MessageSource messageSource) {
        this.resetService = resetService;
        this.emailService = emailService;
        this.userRepo = userRepo;
        this.rateLimiter = rateLimiter;
        this.messageSource = messageSource;
    }

    /** Endpoint para solicitar el email de reseteo (respuesta neutra y rate limiting) */
    @PostMapping("/request")
    @ResponseBody
    public ResponseEntity<?> requestReset(@RequestParam("email") String email, HttpServletRequest req, Locale locale) {
        String clientIp = extractClientIp(req);

        // RATE LIMIT: 5/15min por IP -> si se supera, 429 con mensaje neutro
        if (!rateLimiter.tryConsume("reset:" + clientIp)) {
            // No revelamos nada concreto
            return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS)
                    .body(Map.of("message",
                    messageSource.getMessage("auth.reset.request.success", null, locale)));
        }

        userRepo.findByUserNameIgnoreCase(email).ifPresent(user -> {
            String token = resetService.createTokenForUser(
                    user.getId(),
                    1, // caduca en 1h
                    clientIp,
                    req.getHeader("User-Agent")
            );
            String resetLink = buildResetLink(req, token);
            try {
                emailService.sendPasswordResetMail(user.getUserName(), user.getFullName(), resetLink, locale);
            } catch (Exception ignored) {
                // Deliberadamente no variamos la respuesta para no filtrar info
            }
        });

        // Respuesta neutra SIEMPRE en 200 (si no fue 429)
        return ResponseEntity.ok(Map.of("message",
                messageSource.getMessage("auth.reset.request.success", null, locale)));
    }

    /** Muestra formulario si el token es válido */
    @GetMapping("/reset")
    public String showResetForm(@RequestParam("token") String token, Model model) {
        Long userId = resetService.validateTokenAndGetUserId(token);
        if (userId == null) {
            model.addAttribute("invalidToken", true);
            return "auth/reset-invalid";
        }
        model.addAttribute("token", token);
        return "auth/reset-form";
    }

    /** Procesa y guarda nueva contraseña */
    @PostMapping("/reset")
    public String handleReset(
            @RequestParam("token") String token,
            @RequestParam("password") @NotBlank String password,
            @RequestParam("confirmPassword") @NotBlank String confirmPassword,
            Model model, Locale locale
    ) {
        if (!password.equals(confirmPassword)) {
            model.addAttribute("token", token);
            model.addAttribute("error", messageSource.getMessage("auth.reset.form.passwordsMismatch", null, locale));
            return "auth/reset-form";
        }

        try {
            resetService.consumeTokenAndSetPassword(token, password);
            return "redirect:/login?resetOk";
        } catch (IllegalArgumentException ex) {
            model.addAttribute("invalidToken", true);
            return "auth/reset-invalid";
        }
    }

    private static String extractClientIp(HttpServletRequest req) {
        // Soporte detrás de proxy
        String xff = req.getHeader("X-Forwarded-For");
        if (xff != null && !xff.isBlank()) {
            // coger el primer IP de la cadena
            return xff.split(",")[0].trim();
        }
        String realIp = req.getHeader("X-Real-IP");
        if (realIp != null && !realIp.isBlank()) return realIp.trim();
        return req.getRemoteAddr();
    }

    private static String buildResetLink(HttpServletRequest req, String token) {
        String scheme = req.getHeader("X-Forwarded-Proto");
        if (scheme == null || scheme.isBlank()) scheme = req.getScheme(); // http/https
        String host = req.getHeader("Host"); // respeta el host público bajo proxy
        if (host == null || host.isBlank()) host = req.getServerName() + (req.getServerPort() != 80 && req.getServerPort() != 443 ? ":" + req.getServerPort() : "");
        String ctx = req.getContextPath() == null ? "" : req.getContextPath();
        return scheme + "://" + host + ctx + "/password/reset?token=" + token;
    }
}
