package ru.yandex.direct.api.v5.entity.keywords.delegate;

import java.util.List;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import com.yandex.direct.api.v5.general.ActionResult;
import com.yandex.direct.api.v5.keywords.UpdateRequest;
import com.yandex.direct.api.v5.keywords.UpdateResponse;
import org.springframework.stereotype.Service;

import ru.yandex.direct.api.v5.converter.ResultConverter;
import ru.yandex.direct.api.v5.entity.OperationOnListDelegate;
import ru.yandex.direct.api.v5.entity.keywords.KeywordsEndpoint;
import ru.yandex.direct.api.v5.entity.keywords.container.UpdateInputItem;
import ru.yandex.direct.api.v5.entity.keywords.converter.KeywordsUpdateRequestConverter;
import ru.yandex.direct.api.v5.entity.keywords.validation.KeywordsUpdateRequestValidator;
import ru.yandex.direct.api.v5.result.ApiMassResult;
import ru.yandex.direct.api.v5.result.ApiResult;
import ru.yandex.direct.api.v5.security.ApiAuthenticationSource;
import ru.yandex.direct.api.v5.validation.DefectType;
import ru.yandex.direct.core.entity.keyword.model.Keyword;
import ru.yandex.direct.core.entity.relevancematch.model.RelevanceMatch;
import ru.yandex.direct.core.entity.user.model.ApiUser;
import ru.yandex.direct.tracing.Trace;
import ru.yandex.direct.tracing.TraceProfile;
import ru.yandex.direct.validation.result.MappingPathConverter;
import ru.yandex.direct.validation.result.PathConverter;
import ru.yandex.direct.validation.result.ValidationResult;

import static ru.yandex.direct.utils.FunctionalUtils.mapList;

@Service
public class UpdateKeywordsDelegate
        extends OperationOnListDelegate<UpdateRequest, UpdateResponse, UpdateInputItem, Long> {
    private static final PathConverter UPDATE_KEYWORDS_PATH_CONVERTER =
            MappingPathConverter.builder(UpdateKeywordsDelegate.class, "capitalize")
                    .add(KeywordsEndpoint.getKeywordsCommonDict())
                    .add(Keyword.ID.name(), "Id")
                    .add(RelevanceMatch.RELEVANCE_MATCH_CATEGORIES.name(), "AutotargetingCategories")
                    .ignore(Keyword.AD_GROUP_ID.name())
                    .build();

    private final ResultConverter resultConverter;
    private final KeywordsUpdateRequestValidator requestValidator;
    private final KeywordsUpdateRequestConverter keywordsUpdateRequestConverter;
    private final KeywordsUpdater keywordsUpdater;

    public UpdateKeywordsDelegate(ApiAuthenticationSource auth,
                                  ResultConverter resultConverter,
                                  KeywordsUpdateRequestValidator requestValidator,
                                  KeywordsUpdateRequestConverter keywordsUpdateRequestConverter,
                                  KeywordsUpdater keywordsUpdater) {
        super(UPDATE_KEYWORDS_PATH_CONVERTER, auth);
        this.resultConverter = resultConverter;
        this.requestValidator = requestValidator;
        this.keywordsUpdateRequestConverter = keywordsUpdateRequestConverter;
        this.keywordsUpdater = keywordsUpdater;
    }

    @Nullable
    @Override
    public ValidationResult<UpdateRequest, DefectType> validateRequest(UpdateRequest externalRequest) {
        try (TraceProfile ignore = Trace.current().profile("keywords.update:validateRequest")) {
            return requestValidator.validate(externalRequest);
        }
    }

    @Override
    public List<UpdateInputItem> convertRequest(UpdateRequest externalRequest) {
        try (TraceProfile ignore = Trace.current().profile("keywords.update:convertRequest")) {
            ApiUser targetUser = auth.getChiefSubclient();
            return keywordsUpdateRequestConverter.convertRequest(externalRequest, targetUser.getClientId());
        }
    }

    @Override
    @Nonnull
    public ValidationResult<List<UpdateInputItem>, DefectType> validateInternalRequest(
            List<UpdateInputItem> internalRequest) {
        return requestValidator.validateInternalRequest(internalRequest);
    }

    @Override
    public ApiMassResult<Long> processList(List<UpdateInputItem> internalRequest) {
        try (TraceProfile ignore = Trace.current().profile("keywords.update:processRequest")) {
            return keywordsUpdater.doUpdate(internalRequest);
        }
    }

    @Override
    public UpdateResponse convertResponse(ApiResult<List<ApiResult<Long>>> result) {
        try (TraceProfile ignore = Trace.current().profile("keywords.update:convertResponse")) {
            List<ActionResult> updateResults = mapList(result.getResult(), r -> resultConverter.toActionResult(
                    r, apiPathConverter, ActionResult::new, ActionResult::setId));
            return new UpdateResponse().withUpdateResults(updateResults);
        }
    }
}
