package de.szut.casino.security.service;

import de.szut.casino.exceptionHandling.exceptions.EmailNotVerifiedException;
import de.szut.casino.security.dto.AuthResponseDto;
import de.szut.casino.security.dto.LoginRequestDto;
import de.szut.casino.security.dto.ResetPasswordDto;
import de.szut.casino.security.jwt.JwtUtils;
import de.szut.casino.user.UserEntity;
import de.szut.casino.user.UserService;
import de.szut.casino.user.dto.CreateUserDto;
import de.szut.casino.user.dto.GetUserDto;
import jakarta.mail.MessagingException;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Optional;

@Service
public class AuthService {

    private final AuthenticationManager authenticationManager;
    private final JwtUtils jwtUtils;
    private final UserService userService;
    private final EmailService emailService;
    private final PasswordEncoder passwordEncoder;

    public AuthService(AuthenticationManager authenticationManager, JwtUtils jwtUtils, UserService userService, EmailService emailService, PasswordEncoder passwordEncoder) {
        this.authenticationManager = authenticationManager;
        this.jwtUtils = jwtUtils;
        this.userService = userService;
        this.emailService = emailService;
        this.passwordEncoder = passwordEncoder;
    }

    public AuthResponseDto login(LoginRequestDto loginRequest) throws EmailNotVerifiedException {
        if (!userService.isVerified(loginRequest.getUsernameOrEmail())) {
            throw new EmailNotVerifiedException();
        }

        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        loginRequest.getUsernameOrEmail(),
                        loginRequest.getPassword()));

        SecurityContextHolder.getContext().setAuthentication(authentication);
        String jwt = jwtUtils.generateToken(authentication);

        return new AuthResponseDto(jwt);
    }

    public GetUserDto register(CreateUserDto signUpRequest) throws MessagingException, IOException {
        UserEntity user = userService.createUser(signUpRequest);

        this.emailService.sendEmailVerificationEmail(user);

        return new GetUserDto(
                user.getId(),
                user.getEmail(),
                user.getUsername(),
                user.getBalance()
        );
    }

    public Boolean verifyEmail(String token) throws MessagingException, IOException {
        Optional<UserEntity> optionalUser = userService.getUserByVerificationToken(token);

        if (!optionalUser.isPresent()) {
            return false;
        }

        UserEntity user = optionalUser.get();

        user.setEmailVerified(true);
        user.setVerificationToken(null);
        userService.saveUser(user);
        this.emailService.sendWelcomeEmail(user);

        return true;
    }

    public void recoverPassword(String email) throws MessagingException, IOException {
        Optional<UserEntity> optionalUser = userService.getUserByEmail(email);

        if (optionalUser.isPresent()) {
            UserEntity user = optionalUser.get();
            user.setPasswordResetToken(RandomStringUtils.randomAlphanumeric(64));
            userService.saveUser(user);
            this.emailService.sendPasswordRecoveryEmail(user);
        }
    }

    public void resetPassword(ResetPasswordDto passwordDto) {
        Optional<UserEntity> optionalUser = userService.getUserByPasswordResetToken(passwordDto.getToken());

        if (optionalUser.isPresent()) {
            UserEntity user = optionalUser.get();
            user.setPassword(passwordEncoder.encode(passwordDto.getPassword()));
            user.setPasswordResetToken(null);
            userService.saveUser(user);
        }
    }
}
