package ru.yandex.direct.web.entity.smsauth.service;

import javax.annotation.ParametersAreNonnullByDefault;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import ru.yandex.direct.asynchttp.Result;
import ru.yandex.direct.core.entity.user.service.UserService;
import ru.yandex.direct.core.smsauth.model.SmsAuthKey;
import ru.yandex.direct.core.smsauth.service.SmsAuthStorageService;
import ru.yandex.direct.web.entity.smsauth.model.SmsAuthBaseInternalRequest;
import ru.yandex.direct.web.entity.smsauth.model.checkpassword.CheckPasswordInternalRequest;
import ru.yandex.direct.web.entity.smsauth.model.getsmsphone.GetSmsPhoneResult;
import ru.yandex.direct.web.entity.smsauth.model.sendsms.SendSmsByPhoneIdInternalRequest;
import ru.yandex.direct.web.entity.smsauth.model.sendsms.SendSmsResult;
import ru.yandex.direct.yasms.YaSmsClient;
import ru.yandex.direct.yasms.model.YaSmsSendSmsResponse;
import ru.yandex.inside.passport.blackbox2.protocol.response.BlackboxPhone;

import static ru.yandex.direct.integrations.configuration.IntegrationsConfiguration.YA_SMS_CLIENT_WEB;
import static ru.yandex.direct.web.entity.smsauth.SmsAuthUtils.convert;
import static ru.yandex.direct.web.entity.smsauth.SmsAuthUtils.generateRandomSmsPassword;
import static ru.yandex.direct.web.entity.smsauth.SmsAuthUtils.getSmsMessage;

/**
 * Сервис для SMS аутетификации.
 */
@ParametersAreNonnullByDefault
@Service
public class SmsAuthService {

    private final UserService userService;
    private final SmsAuthStorageService smsAuthStorageService;
    private final YaSmsClient yaSmsClient;

    @Autowired
    public SmsAuthService(UserService userService,
                          SmsAuthStorageService smsAuthStorageService,
                          @Qualifier(YA_SMS_CLIENT_WEB) YaSmsClient yaSmsClient) {
        this.userService = userService;
        this.smsAuthStorageService = smsAuthStorageService;
        this.yaSmsClient = yaSmsClient;
    }

    /**
     * Возвращает информацию о телефоне пользователя для отправки смс с кодом.
     *
     * @param operatorUid uid оператора
     * @return {@link GetSmsPhoneResult}, содержащий номер телефона в обфусцированном виде и id этого номера телефона
     */
    public GetSmsPhoneResult getSmsPhone(Long operatorUid) {
        BlackboxPhone smsBlackboxPhone = userService.getSmsBlackboxPhone(operatorUid);

        return smsBlackboxPhone != null ?
                new GetSmsPhoneResult(smsBlackboxPhone.getMaskedE164Number().getOrNull(), smsBlackboxPhone.getPhoneId())
                : new GetSmsPhoneResult();
    }

    /**
     * Метод для инциализации попытки аутентификации и отправки кода через смс. Состоит из этапов:
     * 1. Инциализация попытки аутентификации. На этом этапе генерируется случайный пароль и все нужные данные об
     * аутентификации (напр. сам пароль в захэшированном виде и подсоленный, количество истраченных попыток ввода
     * пароля и т.п.) кладутся в Redis.
     * 2. Отправка смс с паролем и конвертация ответа от сервиса SmsAuth в формат Директа.
     *
     * @param request запрос попытки аутентификации
     * @return ответа от сервиса SmsAuth в формате Директа
     */
    public SendSmsResult sendSms(SendSmsByPhoneIdInternalRequest request) {
        String smsPassword = generateRandomSmsPassword();

        SmsAuthKey key = SmsAuthKey.ofKeyParts(request.getOperatorUid(), request.getSessionIdHash());
        boolean authAttemptInitSuccess = smsAuthStorageService.initAuthAttempt(key,
                request.getSessionIdHash(), smsPassword);
        if (!authAttemptInitSuccess) {
            throw new IllegalArgumentException("Sms authentication attempt failed");
        }

        Result<YaSmsSendSmsResponse> response =
                yaSmsClient.sendSms(request.getOperatorUid(), request.getPhoneId(), getSmsMessage(smsPassword));
        if (response.getSuccess() == null) {
            throw new IllegalArgumentException("Failed to send sms");
        }

        return convert(response.getSuccess());
    }

    /**
     * Успешно завершает процесс аутентификации через смс.
     *
     * @param request запрос на попытку ввода пароля
     */
    public void completeAuthentication(CheckPasswordInternalRequest request) {
        SmsAuthKey key = SmsAuthKey.ofKeyParts(request.getOperatorUid(), request.getSessionIdHash());

        smsAuthStorageService.completeAuthentication(key);
    }

    /**
     * Снимает лок с сессии.
     *
     * @param request запрос аутентификации через смс
     */
    public void unlockSession(SmsAuthBaseInternalRequest request) {
        SmsAuthKey key = SmsAuthKey.ofKeyParts(request.getOperatorUid(), request.getSessionIdHash());

        smsAuthStorageService.unlockSession(key);
    }
}
