package ru.yandex.direct.api.v5.entity.clients.service;

import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import javax.annotation.ParametersAreNonnullByDefault;

import com.yandex.direct.api.v5.clients.GetSupportPinRequest;
import com.yandex.direct.api.v5.clients.GetSupportPinResponse;
import com.yandex.direct.api.v5.clients.SupportPinFieldEnum;
import one.util.streamex.StreamEx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import ru.yandex.direct.api.v5.context.ApiContextHolder;
import ru.yandex.direct.api.v5.entity.BaseApiServiceDelegate;
import ru.yandex.direct.api.v5.entity.clients.exceptions.InvalidOperatorRoleException;
import ru.yandex.direct.api.v5.entity.clients.exceptions.RequiredOauthTokenException;
import ru.yandex.direct.api.v5.entity.clients.exceptions.UnableToGetPinException;
import ru.yandex.direct.api.v5.logging.ApiLogRecord;
import ru.yandex.direct.api.v5.result.ApiResult;
import ru.yandex.direct.api.v5.security.ApiAuthenticationSource;
import ru.yandex.direct.core.entity.user.model.User;
import ru.yandex.direct.core.service.integration.passport.PassportService;
import ru.yandex.direct.tracing.Trace;
import ru.yandex.direct.utils.DateTimeUtils;
import ru.yandex.direct.validation.result.MappingPathConverter;
import ru.yandex.inside.passport.internal.api.models.supportcode.CreateSupportCodeResponse;

import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import static ru.yandex.direct.utils.CommonUtils.ifNotNull;
import static ru.yandex.direct.utils.CommonUtils.nvl;

@Component
@ParametersAreNonnullByDefault
public class GetClientsSupportPinDelegate
        extends BaseApiServiceDelegate<GetSupportPinRequest, GetSupportPinResponse, Set<SupportPinFieldEnum>, CreateSupportCodeResponse>
{
    private static final Logger logger = LoggerFactory.getLogger(GetClientsSupportPinDelegate.class);

    private final ApiContextHolder apiContextHolder;
    private final PassportService passportService;

    @Autowired
    public GetClientsSupportPinDelegate(ApiAuthenticationSource auth,
            ApiContextHolder apiContextHolder, PassportService passportService)
    {
        super(MappingPathConverter.builder(GetClientsSupportPinDelegate.class, "emptyConverter").build(), auth);
        this.apiContextHolder = apiContextHolder;
        this.passportService = passportService;
    }

    @Override
    public Set<SupportPinFieldEnum> convertRequest(GetSupportPinRequest request) {
        return StreamEx.of(request.getFieldNames()).toSet();
    }

    @Override
    public ApiResult<List<CreateSupportCodeResponse>> processRequest(Set<SupportPinFieldEnum> fieldNames) {
        User subclient = auth.getSubclient();

        if (!auth.getOperator().isClient()){
            throw new InvalidOperatorRoleException();
        }

        String oauthToken = ifNotNull(apiContextHolder.get().getAuthRequest(), r -> r.getCredentials().getOauthToken());
        if (isNull(oauthToken)) {
            throw new RequiredOauthTokenException();
        }

        Long requestId = ifNotNull(apiContextHolder.get().getApiLogRecord(), ApiLogRecord::getRequestId);
        String reqId = String.valueOf(nvl(requestId, Trace.current().getSpanId()));

        CreateSupportCodeResponse supportCodeInfo =
                passportService.createSupportCode(reqId, subclient.getUid(), oauthToken);

        if (isNull(supportCodeInfo)) {
            throw new UnableToGetPinException();
        }

        unsetNotRequstedFields(supportCodeInfo, fieldNames);

        ApiResult<CreateSupportCodeResponse> successful = ApiResult.successful(supportCodeInfo);//
        return ApiResult.successful(Collections.singletonList(supportCodeInfo));
    }

    private void unsetNotRequstedFields( CreateSupportCodeResponse supportCodeInfo, Set<SupportPinFieldEnum> fieldNames) {
        if (!fieldNames.contains(SupportPinFieldEnum.EXPIRES)){
            supportCodeInfo.setExpires(null);
        }

        if (!fieldNames.contains(SupportPinFieldEnum.PIN)){
            supportCodeInfo.setSupportCode(null);
        }
    }

    @Override
    public GetSupportPinResponse convertResponse(ApiResult<List<CreateSupportCodeResponse>> result) {
        GetSupportPinResponse response = new GetSupportPinResponse();

        CreateSupportCodeResponse pinCodeInfo = result.getResult().get(0);
        if (nonNull(pinCodeInfo)) {
            response.withPin(pinCodeInfo.getSupportCode());
            Long expiresUnixtime = pinCodeInfo.getExpires();
            if (nonNull(expiresUnixtime)) {
                response.setExpires(convertSecondsToStringifiedDateTime(expiresUnixtime));
            }
        }

        return response;
    }

    private String convertSecondsToStringifiedDateTime(Long unixtime) {
        var dateTime = OffsetDateTime.ofInstant(Instant.ofEpochSecond(unixtime), DateTimeUtils.MSK);
        return dateTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
    }
}
