package com.imprimelibros.erp.login;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import org.springframework.context.MessageSource;

import com.imprimelibros.erp.common.email.EmailService;
import com.imprimelibros.erp.login.dto.SignupForm;
import com.imprimelibros.erp.users.Role;
import com.imprimelibros.erp.users.RoleDao;
import com.imprimelibros.erp.users.User;
import com.imprimelibros.erp.users.UserDao;

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

@Service
public class SignupService {

    private final UserDao userRepository;
    private final RoleDao roleRepository;
    private final PasswordEncoder passwordEncoder;
    private final VerificationTokenRepository tokenRepository;
    private final EmailService emailService;
    private final MessageSource messageSource;

    // minutos de validez del token
    private static final long TOKEN_MINUTES = 60;

    public SignupService(UserDao userRepository,
            RoleDao roleRepository,
            PasswordEncoder passwordEncoder,
            VerificationTokenRepository tokenRepository,
            EmailService emailService,
            MessageSource messageSource) {
        this.userRepository = userRepository;
        this.roleRepository = roleRepository;
        this.passwordEncoder = passwordEncoder;
        this.tokenRepository = tokenRepository;
        this.emailService = emailService;
        this.messageSource = messageSource;
    }

    @Transactional
    public void register(SignupForm form, Locale locale) {
        if (!form.getPassword().equals(form.getPasswordConfirm())) {
            throw new IllegalArgumentException(
                    messageSource.getMessage("login.signup.error.password.mismatch", null, locale));
        }

        String email = form.getUsername().trim().toLowerCase();

        var existingLite = userRepository.findLiteByUserNameIgnoreCase(email);

        if (existingLite.isPresent()) {
            var lite = existingLite.get();

            boolean isDeleted = lite.getDeleted();
            if (isDeleted) {
                // caso: existe pero borrado
                throw new IllegalArgumentException(
                        messageSource.getMessage("login.signup.error.email.exists-archived", null, locale));
            } else {
                // caso: existe y no borrado
                throw new IllegalArgumentException(
                        messageSource.getMessage("login.signup.error.email.exists", null, locale));
            }
        }

        // Crear usuario deshabilitado
        User user = new User();
        user.setUserName(form.getUsername().trim().toLowerCase());
        user.setFullName(form.getName().trim());
        user.setPassword(passwordEncoder.encode(form.getPassword()));
        user.setEnabled(false);
        var roles = new HashSet<Role>();
        roles.add(roleRepository.findRoleByName("USER").orElseThrow());
        user.setRoles(roles);
        user = userRepository.save(user);

        // Generar token
        var token = VerificationToken.create(user.getId(), TOKEN_MINUTES);
        tokenRepository.save(token);

        // Construir URL absoluta para /verify
        String verifyUrl = ServletUriComponentsBuilder.fromCurrentContextPath()
                .path("/verify")
                .queryParam("token", token.getToken())
                .build()
                .toUriString();

        // Enviar correo
        Map<String, Object> model = new HashMap<>();
        model.put("verifyUrl", verifyUrl);
        model.put("minutes", TOKEN_MINUTES);
        emailService.sendVerificationEmail(
                user.getUserName(),
                user.getFullName(),
                verifyUrl,
                locale);
    }

    @Transactional
    public boolean verify(String tokenValue) {
        var tokenOpt = tokenRepository.findByToken(tokenValue);
        if (tokenOpt.isEmpty())
            return false;

        var token = tokenOpt.get();
        if (token.isUsed() || token.isExpired())
            return false;

        var user = userRepository.findById(token.getUserId())
                .orElseThrow(() -> new IllegalStateException("Usuario no encontrado para el token"));

        user.setEnabled(true);
        userRepository.save(user);

        token.setUsedAt(java.time.LocalDateTime.now());
        tokenRepository.save(token);

        return true;
    }
}
