package com.codebyte.api.auth.service;

import com.codebyte.api.auth.api.dto.ForgotPasswordRequest;
import com.codebyte.api.auth.api.dto.ResetPasswordRequest;
import com.codebyte.api.auth.domain.TokenType;
import com.codebyte.api.auth.domain.User;
import com.codebyte.api.auth.repository.UserRepository;
import com.codebyte.api.config.AppProperties;
import java.util.Locale;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/** Forgot/reset password flow. */
@Service
@Transactional
public class PasswordResetService {

    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;
    private final TokenService tokenService;
    private final RefreshTokenService refreshTokenService;
    private final MailService mailService;
    private final AppProperties properties;

    public PasswordResetService(
            UserRepository userRepository,
            PasswordEncoder passwordEncoder,
            TokenService tokenService,
            RefreshTokenService refreshTokenService,
            MailService mailService,
            AppProperties properties) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
        this.tokenService = tokenService;
        this.refreshTokenService = refreshTokenService;
        this.mailService = mailService;
        this.properties = properties;
    }

    /**
     * Starts a reset for the email if it exists, invalidating any prior reset tokens and emailing a
     * fresh one. Does nothing observable if the email is unknown — the caller always gets 202.
     */
    public void forgot(ForgotPasswordRequest request) {
        String email = normalize(request.email());
        userRepository
                .findByEmailIgnoreCase(email)
                .ifPresent(
                        user -> {
                            tokenService.invalidateActive(user, TokenType.PASSWORD_RESET);
                            String token =
                                    tokenService.issue(
                                            user,
                                            TokenType.PASSWORD_RESET,
                                            properties.reset().ttl());
                            mailService.sendPasswordResetEmail(email, token);
                        });
    }

    /**
     * Completes a reset: consumes the token, sets the new password, and revokes every refresh token
     * for the user so existing sessions are killed.
     */
    public void reset(ResetPasswordRequest request) {
        User user = tokenService.consume(request.token(), TokenType.PASSWORD_RESET);
        user.setPassword(passwordEncoder.encode(request.newPassword()));
        // Persist the new password explicitly before the bulk revokes below, whose
        // clearAutomatically would otherwise detach the entity and drop the change.
        userRepository.saveAndFlush(user);
        tokenService.invalidateActive(user, TokenType.PASSWORD_RESET);
        refreshTokenService.revokeAll(user);
    }

    private static String normalize(String email) {
        return email.trim().toLowerCase(Locale.ROOT);
    }
}
