package ru.soune.nocopy.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import ru.soune.nocopy.dto.BaseRequest;
import ru.soune.nocopy.dto.BaseResponse;
import ru.soune.nocopy.dto.MessageCode;
import ru.soune.nocopy.dto.file.ActionResponse;
import ru.soune.nocopy.dto.register.ResetPasswordRequest;
import ru.soune.nocopy.dto.register.ResetPasswordResponse;
import ru.soune.nocopy.entity.user.AuthToken;
import ru.soune.nocopy.entity.user.EmailVerificationToken;
import ru.soune.nocopy.entity.user.User;
import ru.soune.nocopy.exception.NotValidFieldException;
import ru.soune.nocopy.exception.UserNotFoundException;
import ru.soune.nocopy.repository.AuthTokenRepository;
import ru.soune.nocopy.repository.EmailVerificationTokenRepository;
import ru.soune.nocopy.repository.UserRepository;
import ru.soune.nocopy.service.mail.EmailService;
import ru.soune.nocopy.service.register.AuthService;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Component
@Slf4j
@RequiredArgsConstructor
public class ResetPasswordHandler implements RequestHandler{

    private final ObjectMapper objectMapper;

    private final EmailService emailService;

    private final AuthService authService;

    private final UserRepository userRepository;

    private final EmailVerificationTokenRepository emailVerificationTokenRepository;

    private final AuthTokenRepository authTokenRepository;

    private final PasswordEncoder passwordEncoder;

    @Override
    public BaseResponse handle(BaseRequest request) throws Exception {
        ResetPasswordRequest resetPasswordRequest = objectMapper.convertValue(request.getMessageBody(),
                ResetPasswordRequest.class);
        String action = resetPasswordRequest.getAction();
        User user = userRepository.findByEmail(resetPasswordRequest.getEmail());
        AuthToken authToken;

        if (user == null) {
            throw new UserNotFoundException("User not found with email: " + resetPasswordRequest.getEmail());
        }

        switch (action) {
            case "resetPassword":
                authToken = authService.generateAuthToken(user);

                EmailVerificationToken emailToken = emailService.createEmailVerificationToken(authToken);

                emailService.sendResetPasswordEmail(authToken.getUser(), emailToken.getToken());

                return successResponse(request, authToken, user, MessageCode.SUCCESS.getDescription());
            case "confirmVerification":
                String verifyToken = resetPasswordRequest.getVerifyToken();

                EmailVerificationToken verificationToken =
                        emailVerificationTokenRepository.findByUserIdAndToken(user.getId(), verifyToken);

                if (verificationToken == null) {
                    return new BaseResponse(request.getMsgId(), MessageCode.INVALID_TOKEN.getCode(),
                            MessageCode.INVALID_TOKEN.getDescription(), Map.of("verifyToken", verifyToken));
                }

                List<AuthToken> authTokens = authTokenRepository.findByUserId(user.getId());

                authToken = authTokens.isEmpty() ? authService.generateAuthToken(user): authTokens.getFirst();

                user.setPassword(passwordEncoder.encode(resetPasswordRequest.getPassword()));
                user.setActive(true);

                emailVerificationTokenRepository.delete(verificationToken);

                User saveUser = userRepository.save(user);

                return successResponse(request, authToken, saveUser, MessageCode.SUCCESS.getDescription());
            default:
                ActionResponse response = ActionResponse.builder()
                        .action(action)
                        .availableActions(Arrays.asList("confirmVerification", "resetPassword"))
                        .build();

                return new BaseResponse(request.getMsgId(), MessageCode.INVALID_ACTION.getCode(),
                        "Invalid action: " + action, response);

        }
    }

    private BaseResponse successResponse(BaseRequest request, AuthToken authToken, User user, String message) {
        ResetPasswordResponse resetPasswordResponse = new ResetPasswordResponse();
        resetPasswordResponse.setAuthToken(authToken.getToken());
        resetPasswordResponse.setUserId(user.getId());

        return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), message, resetPasswordResponse);
    }
}