package ru.soune.nocopy.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.BindingResult;
import ru.soune.ReferralService;
import ru.soune.nocopy.dto.*;
import ru.soune.nocopy.dto.register.RegAnswer;
import ru.soune.nocopy.dto.register.RegRequest;
import ru.soune.nocopy.entity.referral.Referral;
import ru.soune.nocopy.entity.user.AuthToken;
import ru.soune.nocopy.entity.user.EmailVerificationToken;
import ru.soune.nocopy.exception.CompanyAlreadyExist;
import ru.soune.nocopy.exception.NotValidFieldException;
import ru.soune.nocopy.exception.ValidationException;
import ru.soune.nocopy.handler.validator.RegRequestValidator;
import ru.soune.nocopy.repository.ReferralJpaRepository;
import ru.soune.nocopy.repository.UserRepository;
import ru.soune.nocopy.service.register.AuthService;
import ru.soune.nocopy.service.mail.EmailService;
import ru.soune.nocopy.service.user.AdditionalInfoService;

import java.util.Arrays;
import java.util.Map;

@Slf4j
@Component
@RequiredArgsConstructor
public class RegRequestHandler implements RequestHandler {
    private final UserRepository userRepository;

    private final RegRequestValidator regRequestValidator;

    private final AuthService authService;

    private final ObjectMapper objectMapper;

    private final EmailService emailService;

    private final ReferralService referralService;

    private final ReferralJpaRepository referralJpaRepository;

    private final AdditionalInfoService additionalInfoService;

    @Override
    public BaseResponse handle(BaseRequest request) throws ValidationException {
        RegRequest regRequest = objectMapper.convertValue(request.getMessageBody(), RegRequest.class);
        try {
            if (userRepository.existsByEmail(regRequest.getEmail()) || userRepository.existsByPhone(regRequest.getPhone())) {
                RegAnswer regAnswer = new RegAnswer();
                regAnswer.setFieldErrors(Arrays.asList(Map.of("email", regRequest.getEmail())));
                regAnswer.setFieldErrors(Arrays.asList(Map.of("phone", regRequest.getPhone())));

                throw new NotValidFieldException("User already exists with email:" + regRequest.getEmail() + " or phone: " +
                        regRequest.getPhone(), new BaseResponse(request.getMsgId(),
                        MessageCode.REG_EMAIL_OR_PHONE_EXISTS.getCode(),
                        MessageCode.REG_EMAIL_OR_PHONE_EXISTS.getDescription(), regAnswer));
            }

            String referralLink = regRequest.getReferralLink();
            String mailVerified = regRequest.getMailVerified();

            if (referralLink != null) {
                Referral referral = referralJpaRepository.findByReferralLink(referralLink);
                if (referral == null) {
                    throw new NotValidFieldException("Referral link is not found:" + referralLink +
                            referralLink, new BaseResponse(request.getMsgId(),
                            MessageCode.REFERRAL_LINK_IS_NOT_EXIST.getCode(),
                            MessageCode.REFERRAL_LINK_IS_NOT_EXIST.getDescription(), Map.of(
                            "referralLink", referralLink)));
                }

                if (mailVerified == null) {
                    throw new NotValidFieldException("Mailverified is null:" + referralLink +
                            referralLink, new BaseResponse(request.getMsgId(),
                            MessageCode.MAIL_VERIFIED_NULL.getCode(),
                            MessageCode.MAIL_VERIFIED_NULL.getDescription(), Map.of(
                            "mailVerified", mailVerified)));
                }
            }

            BindingResult bindingResult = new BeanPropertyBindingResult(regRequest, "regRequest");

            regRequestValidator.validate(regRequest, bindingResult);

            if (bindingResult.hasErrors()) {
                throw new ValidationException(bindingResult, request.getMsgId());
            }

            boolean verified = mailVerified.equals("true");

            AuthToken authToken = authService.register(regRequest, verified);
            EmailVerificationToken emailToken = emailService.createEmailVerificationToken(authToken);
            RegAnswer regAnswer = new RegAnswer();

            if (verified) {
                try {
                    emailService.sendVerificationEmail(authToken.getUser(), emailToken.getToken());
                } catch (Exception e) {
                    return new BaseResponse(request.getMsgId(), MessageCode.SEND_EMAIL_EXCEPTION.getCode(),
                            MessageCode.SEND_EMAIL_EXCEPTION.getDescription(), Map.of(
                            "message", "not send",
                            "email", regRequest.getEmail()
                    ));
                }

                regAnswer.setVerified(false);
                regAnswer.setActive(false);
            } else {
                regAnswer.setUserId(authToken.getUser().getId());

                regAnswer.setVerified(true);
                regAnswer.setActive(true);
            }

            String token = authToken.getToken();
            authService.useUserAuthToken(token);

            regAnswer.setToken(token);
            regAnswer.setUserId(authToken.getUser().getId());

            if (regRequest.getAccountType().equals("b2c")) {
                referralService.onRegister(authToken.getUser().getId(), regRequest.getReferralLink());
            }

            additionalInfoService.additionalInfo(regRequest.getIp(), regRequest.getUserAgent(), authToken.getUser());

            return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(),
                    MessageCode.SUCCESS.getDescription(), regAnswer);
        } catch (CompanyAlreadyExist e) {
            return new BaseResponse(request.getMsgId(), MessageCode.COMPANY_ALREADY_EXISTS.getCode(),
                    MessageCode.COMPANY_ALREADY_EXISTS.getDescription(), Map.of(
                    "inn", regRequest.getInn()));
        }
    }
}