package ru.yandex.autotests.directapi.steps.banners;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.google.common.collect.ImmutableMap;
import com.yandex.direct.api.v5.general.ActionResult;
import com.yandex.direct.api.v5.general.AutotargetingCategoriesEnum;
import com.yandex.direct.api.v5.general.PriorityEnum;
import com.yandex.direct.api.v5.general.StateEnum;
import com.yandex.direct.api.v5.general.StatusEnum;
import com.yandex.direct.api.v5.keywords.AddResponse;
import com.yandex.direct.api.v5.keywords.DeleteResponse;
import com.yandex.direct.api.v5.keywords.GetResponse;
import com.yandex.direct.api.v5.keywords.KeywordFieldEnum;
import com.yandex.direct.api.v5.keywords.KeywordGetItem;
import com.yandex.direct.api.v5.keywords.KeywordsPort;
import com.yandex.direct.api.v5.keywords.ResumeResponse;
import com.yandex.direct.api.v5.keywords.SuspendResponse;
import com.yandex.direct.api.v5.keywords.UpdateResponse;
import org.apache.commons.beanutils.BeanMap;
import org.apache.commons.lang.StringUtils;
import org.hamcrest.Matcher;

import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.BidsStatusmoderate;
import ru.yandex.autotests.direct.db.models.jooq.ppc.tables.records.BidsRecord;
import ru.yandex.autotests.directapi.apiclient.RequestHeader;
import ru.yandex.autotests.directapi.apiclient.config.ConnectionConfig;
import ru.yandex.autotests.directapi.apiclient.errors.Api5Error;
import ru.yandex.autotests.directapi.apiclient.version5.ServiceNames;
import ru.yandex.autotests.directapi.darkside.datacontainers.jsonrpc.fake.GroupFakeInfo;
import ru.yandex.autotests.directapi.exceptions.DirectAPIException;
import ru.yandex.autotests.directapi.model.api5.Action;
import ru.yandex.autotests.directapi.model.api5.general.ExpectedResult;
import ru.yandex.autotests.directapi.model.api5.general.IdsCriteriaMap;
import ru.yandex.autotests.directapi.model.api5.general.JavaOrPerlApi5Error;
import ru.yandex.autotests.directapi.model.api5.general.JavaOrPerlExpectedResult;
import ru.yandex.autotests.directapi.model.api5.general.LimitOffsetMap;
import ru.yandex.autotests.directapi.model.api5.keywords.AddRequestMap;
import ru.yandex.autotests.directapi.model.api5.keywords.DeleteRequestMap;
import ru.yandex.autotests.directapi.model.api5.keywords.GetRequestMap;
import ru.yandex.autotests.directapi.model.api5.keywords.KeywordAddMap;
import ru.yandex.autotests.directapi.model.api5.keywords.KeywordGetItemMap;
import ru.yandex.autotests.directapi.model.api5.keywords.KeywordUpdateMap;
import ru.yandex.autotests.directapi.model.api5.keywords.KeywordsSelectionCriteriaMap;
import ru.yandex.autotests.directapi.model.api5.keywords.ResumeRequestMap;
import ru.yandex.autotests.directapi.model.api5.keywords.SuspendRequestMap;
import ru.yandex.autotests.directapi.model.api5.keywords.UpdateRequestMap;
import ru.yandex.autotests.directapi.steps.BaseApiSteps;
import ru.yandex.autotests.directapi.steps.ConditionFactories;
import ru.yandex.autotests.directapi.steps.UserSteps;
import ru.yandex.autotests.irt.testutils.RandomUtils;
import ru.yandex.autotests.irt.testutils.allure.LogSteps;
import ru.yandex.qatools.allure.annotations.Step;

import static ch.lambdaj.Lambda.extract;
import static ch.lambdaj.Lambda.having;
import static ch.lambdaj.Lambda.on;
import static ch.lambdaj.Lambda.select;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static ru.yandex.autotests.irt.testutils.allure.TestSteps.assumeThat;

/**
 * Created by chicos on 20.11.14.
 */
public class KeywordsSteps extends BaseApiSteps {
    private LogSteps log = LogSteps.getLogger(this.getClass());

    public static final int KEYWORD_MAX_LENGTH = 35;
    public static final int MINUS_WORD_MAX_LENGTH = 35;
    public static final int PHRASE_MAX_LENGTH = 4096;
    public static final int MAX_PLUS_WORDS_IN_PHRASE = 7;

    // приемлемая длина ключевого слова
    public static final int ACCEPTABLE_KEYWORD_LENGTH = 13;

    //TODO: перенести в RandomUtils?
    public static final String CYRILLIC_CHARS_NO_SPACE = new String(RandomUtils.CYRILYC_CHARS).replace(" ", "");

    private static final Map<String, AutotargetingCategoriesEnum> RELEVANCE_MATCH_CATEGORIES_TO_AUTOTARGETING_CATEGORIES =
            ImmutableMap.<String, AutotargetingCategoriesEnum>builder()
                    .put("exact_mark", AutotargetingCategoriesEnum.EXACT)
                    .put("accessory_mark", AutotargetingCategoriesEnum.ACCESSORY)
                    .put("alternative_mark", AutotargetingCategoriesEnum.ALTERNATIVE)
                    .put("broader_mark", AutotargetingCategoriesEnum.BROADER)
                    .put("competitor_mark", AutotargetingCategoriesEnum.COMPETITOR)
                    .build();

    private static KeywordsSteps _instance;

    private KeywordsSteps(ConnectionConfig connectionConfig, RequestHeader requestHeader) {
        super(connectionConfig, requestHeader);
    }

    public static KeywordsSteps getInstance(ConnectionConfig connectionConfig, RequestHeader requestHeader) {
        if (_instance == null) {
            _instance = new KeywordsSteps(connectionConfig, requestHeader);
        } else {
            _instance.setConnectionConfig(connectionConfig);
            _instance.setRequestHeader(requestHeader);
        }
        return _instance;
    }

    private KeywordsPort keywordsService() {
        return soapClientV5().keywordsService();
    }

    private KeywordsPort keywordsService(String login) {
        return soapClientV5().keywordsService(login);
    }

    @Step("[Keywords]: Add")
    public List<Long> keywordsAdd(String login, AddRequestMap request) {
        List<ActionResult> results = ((AddResponse) defaultClientV5()
                .invokeMethod(ServiceNames.KEYWORDS, login, Action.ADD, request.getBean())).getAddResults();
        lookForResponseExceptions(results);
        return getIDsFromActionResults(results);
    }

    public List<Long> keywordsAdd(String login, KeywordAddMap... keywords) {
        AddRequestMap request = new AddRequestMap()
                .withKeywords(keywords);
        return keywordsAdd(login, request);
    }

    public List<Long> keywordsAdd(KeywordAddMap... keywords) {
        return keywordsAdd(null, keywords);
    }

    public List<Long> addDefaultKeywords(String login, long adGroupID, int amount) {
        KeywordAddMap[] keywordAddMaps = new KeywordAddMap[amount];
        for (int i = 0; i < amount; i++) {
            keywordAddMaps[i] = new KeywordAddMap()
                    .defaultKeyword()
                    .withAdGroupId(adGroupID);
        }
        List<Long> keywordIDs = keywordsAdd(login, keywordAddMaps);
        assumeThat(String.format("добавлено %d ключевых слов", amount), keywordIDs, hasSize(amount));
        return keywordIDs;
    }

    public List<Long> addDefaultKeywords(long adGroupID, int amount) {
        return addDefaultKeywords(null, adGroupID, amount);
    }

    public long addDefaultKeyword(String login, long adGroupID) {
        return addDefaultKeywords(login, adGroupID, 1).get(0);
    }

    public long addDefaultKeyword(long adGroupID) {
        return addDefaultKeyword(null, adGroupID);
    }

    public long addDefaultKeywordWithAutotargetingPrefix(long adGroupID) {
        KeywordAddMap[] keywordAddMaps = new KeywordAddMap[1];
        keywordAddMaps[0] = new KeywordAddMap()
                .defaultKeywordWithAutotargetingPrefix()
                .withAdGroupId(adGroupID);
        List<Long> keywordIDs = keywordsAdd(null, keywordAddMaps);
        assumeThat(String.format("добавлено %d ключевых слов c префиксом ---autotargeting", 1), keywordIDs, hasSize(1));
        return keywordIDs.get(0);
    }

    public long addDefaultKeywordWithBidsAndStrategyPriority(String login, Long adGroupId, Long bid, Long contextBid,
                                                             PriorityEnum strategyPriority) {
        List<Long> keywordIDs = keywordsAdd(login,
                new KeywordAddMap()
                        .defaultKeyword()
                        .withBid(bid)
                        .withContextBid(contextBid)
                        .withStrategyPriority(strategyPriority)
                        .withAdGroupId(adGroupId)
        );
        assumeThat("добавлено одно ключевое слово", keywordIDs, hasSize(1));
        return keywordIDs.get(0);
    }

    public long addDefaultKeywordWithBidsAndStrategyPriority(Long adGroupId, Long bid, Long contextBid,
                                                             PriorityEnum strategyPriority) {
        return addDefaultKeywordWithBidsAndStrategyPriority(null, adGroupId, bid, contextBid, strategyPriority);
    }

    public long addKeyword(String login, long adGroupID, String keyword) {
        List<Long> keywordIDs = keywordsAdd(login, new KeywordAddMap()
                .withKeyword(keyword)
                .withAdGroupId(adGroupID));
        assumeThat("добавлено одно ключевое слово", keywordIDs, hasSize(1));
        return keywordIDs.get(0);
    }

    public long addKeyword(long adGroupID, String keyword) {
        return addKeyword(null, adGroupID, keyword);
    }

    public List<Long> addKeywords(String login, long adGroupID, List<String> keywords) {
        KeywordAddMap[] keywordAddMaps = keywords.stream()
                .map(k -> new KeywordAddMap().withAdGroupId(adGroupID).withKeyword(k)).toArray(KeywordAddMap[]::new);
        List<Long> keywordIDs = keywordsAdd(login, keywordAddMaps);
        assumeThat("добавлены все ключевые слова", keywordIDs, hasSize(keywords.size()));
        return keywordIDs;
    }

    public long addRandomKeyword(long adGroupID) {
        return addKeyword(null, adGroupID, RandomUtils.getString(KEYWORD_MAX_LENGTH));
    }

    public long addAutotargetingWithBidsAndStrategyPriority(String login, long adGroupID, Long bid, Long contextBid,
                                                            PriorityEnum strategyPriority) {
        KeywordAddMap keywordAddMap = new KeywordAddMap().autotargeting(adGroupID);

        if (bid != null) {
            keywordAddMap.withBid(bid);
        }
        if (contextBid != null) {
            keywordAddMap.withContextBid(contextBid);
        }
        if (strategyPriority != null) {
            keywordAddMap.withStrategyPriority(strategyPriority);
        }

        List<Long> keywordIDs = keywordsAdd(login, keywordAddMap);

        assumeThat("добавлен один автотаргетинг", keywordIDs, hasSize(1));
        return keywordIDs.get(0);
    }

    public long addAutotargetingWithBid(String login, long adGroupID, Long bid) {
        return addAutotargetingWithBidsAndStrategyPriority(login, adGroupID, bid, null, null);
    }

    public long addAutotargetingWithBid(long adGroupID, Long bid) {
        return addAutotargetingWithBidsAndStrategyPriority(null, adGroupID, bid, null, null);
    }

    public long addAutotargetingWithBidsAndStrategyPriority(long adGroupID, Long bid, Long contextBid,
                                                            PriorityEnum strategyPriority) {
        return addAutotargetingWithBidsAndStrategyPriority(null, adGroupID, bid, contextBid, strategyPriority);
    }

    public long addAutotargeting(long adGroupID) {
        return addAutotargetingWithBidsAndStrategyPriority(null, adGroupID, null, null, null);
    }

    public long addAutotargeting(String login, long adGroupID) {
        return addAutotargetingWithBidsAndStrategyPriority(login, adGroupID, null, null, null);
    }

    public long addShortAutotargeting(long adGroupID) {
        KeywordAddMap keywordAddMap = new KeywordAddMap().shortAutotargeting(adGroupID);

        List<Long> keywordIDs = keywordsAdd(keywordAddMap);

        assumeThat("добавлен один автотаргетинг", keywordIDs, hasSize(1));
        return keywordIDs.get(0);
    }

    @Step("[Keywords]: Update")
    public List<Long> keywordsUpdate(String login, UpdateRequestMap request) {
        List<ActionResult> results = ((UpdateResponse) defaultClientV5()
                .invokeMethod(ServiceNames.KEYWORDS, login, Action.UPDATE, request.getBean())).getUpdateResults();
        lookForResponseExceptions(results);
        return getIDsFromActionResults(results);
    }

    public List<Long> keywordsUpdate(String login, KeywordUpdateMap... keywords) {
        UpdateRequestMap request = new UpdateRequestMap()
                .withKeywords(keywords);
        return keywordsUpdate(login, request);
    }

    public List<Long> keywordsUpdate(KeywordUpdateMap... keywords) {
        return keywordsUpdate(null, keywords);
    }

    @Step("[Keywords]: Get")
    public List<KeywordGetItem> keywordsGet(String login, GetRequestMap request) {
        List<KeywordGetItem> results = ((GetResponse) defaultClientV5()
                .invokeMethod(ServiceNames.KEYWORDS, login, Action.GET, request.getBean())).getKeywords();
        return results;
    }

    @Step("[Keywords]: Get")
    public GetResponse keywordsGetRawResponse(String login, GetRequestMap request) {
        return ((GetResponse) defaultClientV5()
                .invokeMethod(ServiceNames.KEYWORDS, login, Action.GET, request.getBean()));
    }

    public GetResponse keywordsGetRawResponse(GetRequestMap request) {
        return keywordsGetRawResponse(null, request);
    }

    public List<KeywordGetItem> keywordsGet(String login, List<KeywordFieldEnum> fields, LimitOffsetMap limitOffset,
                                            KeywordsSelectionCriteriaMap criteriaMap) {
        KeywordFieldEnum[] fieldsArray = new KeywordFieldEnum[fields.size()];
        GetRequestMap request = new GetRequestMap()
                .withSelectionCriteria(criteriaMap)
                .withFieldNames(fields.toArray(fieldsArray))
                .withPage(limitOffset);
        return keywordsGet(login, request);
    }

    public List<KeywordGetItem> keywordsGet(GetRequestMap request) {
        return keywordsGet(null, request);
    }

    public List<KeywordGetItem> keywordsGet(KeywordsSelectionCriteriaMap criteriaMap) {
        return keywordsGet(null, Arrays.asList(KeywordFieldEnum.class.getEnumConstants()), null, criteriaMap);
    }

    public List<KeywordGetItem> keywordsGet(String login, KeywordsSelectionCriteriaMap criteriaMap) {
        return keywordsGet(login, Arrays.asList(KeywordFieldEnum.class.getEnumConstants()), null, criteriaMap);
    }

    public List<KeywordGetItem> keywordsGet(List<KeywordFieldEnum> fields, KeywordsSelectionCriteriaMap criteriaMap) {
        return keywordsGet(null, fields, null, criteriaMap);
    }

    public List<KeywordGetItem> keywordsGet(String login, List<KeywordFieldEnum> fields,
                                            KeywordsSelectionCriteriaMap criteriaMap) {
        return keywordsGet(login, fields, null, criteriaMap);
    }

    public List<KeywordGetItem> keywordsGet(LimitOffsetMap limitOffset, KeywordsSelectionCriteriaMap criteriaMap) {
        return keywordsGet(null, Arrays.asList(KeywordFieldEnum.class.getEnumConstants()), limitOffset, criteriaMap);
    }

    public List<KeywordGetItem> keywordsGet(String login, LimitOffsetMap limitOffset,
                                            KeywordsSelectionCriteriaMap criteriaMap) {
        return keywordsGet(login, Arrays.asList(KeywordFieldEnum.class.getEnumConstants()), limitOffset, criteriaMap);
    }

    public List<KeywordGetItem> keywordsGetByIdWithFields(String login, List<KeywordFieldEnum> fields,
                                                          Long... keywordIds) {
        return keywordsGet(login, fields, null, new KeywordsSelectionCriteriaMap().withIds(keywordIds));
    }

    public List<KeywordGetItem> keywordsGetByIdWithFields(List<KeywordFieldEnum> fields, Long... keywordIds) {
        return keywordsGetByIdWithFields(null, fields, keywordIds);
    }

    public List<KeywordGetItem> keywordsGetById(String login, Long... keywordIds) {
        KeywordsSelectionCriteriaMap criteriaMap = new KeywordsSelectionCriteriaMap().withIds(keywordIds);
        return keywordsGet(login, Arrays.asList(KeywordFieldEnum.class.getEnumConstants()), null, criteriaMap);
    }

    public List<KeywordGetItem> keywordsGetById(Long... keywordIds) {
        return keywordsGetById(null, keywordIds);
    }

    public StateEnum getKeywordState(String login, Long keywordId) {
        List<KeywordGetItem> keywords =
                keywordsGetByIdWithFields(login, Arrays.asList(KeywordFieldEnum.STATE), keywordId);
        assumeThat("вернулась одна фраза", keywords.size(), equalTo(1));
        return keywords.get(0).getState();
    }

    public StateEnum getKeywordState(Long keywordId) {
        return getKeywordState(null, keywordId);
    }

    public List<KeywordGetItem> keywordsGetByAdGroupId(String login, Long... adGroupIds) {
        KeywordsSelectionCriteriaMap criteriaMap = new KeywordsSelectionCriteriaMap().withAdGroupIds(adGroupIds);
        return keywordsGet(login, Arrays.asList(KeywordFieldEnum.class.getEnumConstants()), null, criteriaMap);
    }

    public List<KeywordGetItem> keywordsGetByAdGroupId(Long... adGroupIds) {
        return keywordsGetByAdGroupId(null, adGroupIds);
    }

    public List<KeywordGetItem> keywordsGetByCampaignId(String login, Integer... campaignIds) {
        Long[] longIds = new Long[campaignIds.length];
        for (int i = 0; i < campaignIds.length; i++) {
            longIds[i] = campaignIds[i].longValue();
        }
        KeywordsSelectionCriteriaMap criteriaMap = new KeywordsSelectionCriteriaMap().withCampaignIds(longIds);
        return keywordsGet(login, Arrays.asList(KeywordFieldEnum.class.getEnumConstants()), null, criteriaMap);
    }

    public List<KeywordGetItem> keywordsGetByCampaignId(Integer... campaignIds) {
        return keywordsGetByCampaignId(null, campaignIds);
    }

    public List<KeywordGetItem> keywordsGetByCampaignId(String login, Long... campaignIds) {
        return keywordsGetByCampaignId(login, Stream.of(campaignIds).map(Long::intValue).toArray(Integer[]::new));
    }

    @Step("[Keywords]: Delete")
    public List<Long> keywordsDelete(String login, Long... keywordIds) {
        DeleteRequestMap request = new DeleteRequestMap()
                .withSelectionCriteria(
                        new IdsCriteriaMap().withIds(keywordIds));
        List<ActionResult> results = ((DeleteResponse) defaultClientV5()
                .invokeMethod(ServiceNames.KEYWORDS, login, Action.DELETE, request.getBean())).getDeleteResults();
        lookForResponseExceptions(results);
        return getIDsFromActionResults(results);
    }

    public List<Long> keywordsDelete(Long... keywordIds) {
        return keywordsDelete(null, keywordIds);
    }


    @Step("[Keywords]: Suspend")
    public SuspendResponse keywordsSuspend(String login, SuspendRequestMap request) {
        return (SuspendResponse) defaultClientV5()
                .invokeMethod(ServiceNames.KEYWORDS, login, Action.SUSPEND, request.getBean());
    }

    @Step("[Keywords]: Suspend")
    public SuspendResponse keywordsSuspend(SuspendRequestMap request) {
        return keywordsSuspend(null, request);
    }

    @Step
    public List<Long> keywordsSuspend(String login, Long... keywordIds) {
        SuspendRequestMap request = new SuspendRequestMap()
                .withSelectionCriteria(
                        new IdsCriteriaMap().withIds(keywordIds));
        List<ActionResult> results = keywordsSuspend(login, request).getSuspendResults();
        lookForResponseExceptions(results);
        return getIDsFromActionResults(results);
    }

    @Step
    public List<Long> keywordsSuspend(Long... keywordIds) {
        return keywordsSuspend(null, keywordIds);
    }

    @Step("[Keywords]: Resume")
    public List<Long> keywordsResume(String login, Long... keywordIds) {
        ResumeRequestMap request = new ResumeRequestMap()
                .withSelectionCriteria(
                        new IdsCriteriaMap().withIds(keywordIds));
        List<ActionResult> results = ((ResumeResponse) defaultClientV5()
                .invokeMethod(ServiceNames.KEYWORDS, login, Action.RESUME, request.getBean())).getResumeResults();
        lookForResponseExceptions(results);
        return getIDsFromActionResults(results);
    }

    @Step
    public List<Long> keywordsResume(Long... keywordIds) {
        return keywordsResume(null, keywordIds);
    }

    private void lookForResponseExceptions(List<ActionResult> results) {
        List<ActionResult> errors = select(results, having(on(ActionResult.class).getErrors(), is(not(empty()))));
//        List<ActionResult> warnings = select(results, having(on(ActionResult.class).getWarnings(), is(not(empty()))));

        assertThat("Есть ошибки при вызове метода", errors, is(empty()));
//        assertThat("Есть предупреждения при вызове метода", warnings, is(empty()));
    }

    private List<Long> getIDsFromActionResults(List<ActionResult> results) {
        return extract(results, on(ActionResult.class).getId());
    }

    public <T extends BeanMap> void shouldGetKeywordErrorOn(Action action, T request, Api5Error api5Error) {
        shouldGetErrorOn("", ServiceNames.KEYWORDS, null, action, request, api5Error);
    }

    public <T extends BeanMap> void shouldGetKeywordErrorOn(Action action, T request, JavaOrPerlApi5Error api5Error) {
        shouldGetErrorOn("", ServiceNames.KEYWORDS, null, action, request, api5Error);
    }

    public <T extends BeanMap> void shouldGetKeywordErrorOn(String message, Action action, T request,
                                                            Api5Error api5Error) {
        shouldGetErrorOn(message, ServiceNames.KEYWORDS, null, action, request, api5Error);
    }

    public <T extends BeanMap> void shouldGetKeywordErrorOn(Action action, String login, T request,
                                                            Api5Error api5Error) {
        shouldGetErrorOn("", ServiceNames.KEYWORDS, login, action, request, api5Error);
    }

    public <T extends BeanMap> void shouldGetKeywordErrorOn(String message, Action action, String login, T request,
                                                            Api5Error api5Error) {
        shouldGetErrorOn(message, ServiceNames.KEYWORDS, login, action, request, api5Error);
    }

    public <T> void shouldGetKeywordErrorJsonOn(Action action, String login, T request, Api5Error api5Error) {
        shouldGetJSONErrorOn(ServiceNames.KEYWORDS, login, action, request, api5Error);
    }

    public <T> void shouldGetKeywordErrorJsonOn(Action action, T request, Api5Error api5Error) {
        shouldGetJSONErrorOn(ServiceNames.KEYWORDS, null, action, request, api5Error);
    }

    public <T> void shouldGetKeywordErrorJsonOn(Action action, T request, JavaOrPerlApi5Error api5Error) {
        shouldGetJSONErrorOn(ServiceNames.KEYWORDS, null, action, request, api5Error);
    }

    public <T extends BeanMap> void shouldGetResultOn(Action action,
                                                      String login,
                                                      T args,
                                                      ExpectedResult... expectedResults) {
        shouldGetResultOn(ServiceNames.KEYWORDS, login, action, args, expectedResults);
    }

    public <T extends BeanMap> void shouldGetResultOn(Action action,
                                                      String login, T args,
                                                      JavaOrPerlExpectedResult... expectedResults) {
        shouldGetResultOn(ServiceNames.KEYWORDS, login, action, args, expectedResults);
    }

    public <T extends BeanMap> void shouldGetResultOn(Action action,
                                                      T args,
                                                      ExpectedResult... expectedResults) {
        shouldGetResultOn(ServiceNames.KEYWORDS, null, action, args, expectedResults);
    }

    public <T extends BeanMap> void shouldGetResultOn(Action action, T args,
                                                      JavaOrPerlExpectedResult... expectedResults) {
        shouldGetResultOn(ServiceNames.KEYWORDS, null, action, args, expectedResults);
    }

    public <T extends BeanMap> void shouldGetResultOn(Action action,
                                                      T args,
                                                      List<Matcher<? super ActionResult>> expectedResultMatchers) {
        shouldGetResultOn(ServiceNames.KEYWORDS, null, action, args, expectedResultMatchers);
    }

    public AddResponse shouldGetResultOnAdd(String login, AddRequestMap parameters,
                                            ExpectedResult... expectedResults) {
        return (AddResponse) shouldGetResultOn(ServiceNames.KEYWORDS, login, Action.ADD, parameters, expectedResults);
    }

    public SuspendResponse shouldGetResultOnSuspend(String login, SuspendRequestMap parameters,
                                                    ExpectedResult... expectedResults) {
        return (SuspendResponse) shouldGetResultOn(
                ServiceNames.KEYWORDS, login, Action.SUSPEND, parameters, expectedResults);
    }

    public SuspendResponse shouldGetResultOnSuspend(SuspendRequestMap parameters, ExpectedResult... expectedResults) {
        return shouldGetResultOnSuspend(null, parameters, expectedResults);
    }

    public SuspendResponse shouldGetResultOnSuspend(IdsCriteriaMap selectionCriteria,
                                                    ExpectedResult... expectedResults) {
        return shouldGetResultOnSuspend(null, new SuspendRequestMap().withSelectionCriteria(selectionCriteria),
                expectedResults);
    }

    public DeleteResponse shouldGetResultOnDelete(String login, DeleteRequestMap parameters,
                                                  ExpectedResult... expectedResults) {
        return (DeleteResponse) shouldGetResultOn(
                ServiceNames.KEYWORDS, login, Action.DELETE, parameters, expectedResults);
    }

    public DeleteResponse shouldGetResultOnDelete(DeleteRequestMap parameters, ExpectedResult... expectedResults) {
        return shouldGetResultOnDelete(null, parameters, expectedResults);
    }

    public DeleteResponse shouldGetResultOnDelete(IdsCriteriaMap selectionCriteria, ExpectedResult... expectedResults) {
        return shouldGetResultOnDelete(null, new DeleteRequestMap().withSelectionCriteria(selectionCriteria),
                expectedResults);
    }

    public void expectErrorOnKeywordsGet(GetRequestMap parameters, String login, Api5Error api5Error) {
        shouldGetErrorOn(ServiceNames.KEYWORDS, login, Action.GET, parameters, api5Error);
    }

    public void expectErrorOnKeywordsGet(GetRequestMap parameters, Api5Error api5Error) {
        expectErrorOnKeywordsGet(parameters, null, api5Error);
    }

    public void expectErrorOnKeywordsGet(GetRequestMap parameters, JavaOrPerlApi5Error api5Error) {
        shouldGetErrorOn(ServiceNames.KEYWORDS, null, Action.GET, parameters, api5Error);
    }

    public void expectErrorOnKeywordsGet(Object parameters, String login, Api5Error api5Error) {
        shouldGetJSONErrorOn(ServiceNames.KEYWORDS, login, Action.GET, parameters, api5Error);
    }

    public void expectErrorOnKeywordsGet(Object parameters, Api5Error api5Error) {
        expectErrorOnKeywordsGet(parameters, null, api5Error);
    }

    @Step("Keyword: фейково установить ключевому слову статус активации (id = {0}, state = {1})")
    public void setKeywordState(long groupID, long keywordID, int isSuspended, BidsStatusmoderate statusModerate) {
        if ((isSuspended == 0 || isSuspended == 1) && (!KeywordGetItemMap.getAvailableStates()
                .contains(statusModerate))) {
            throw new DirectAPIException(
                    "Некорректный набор параметров: isSuspended = " + isSuspended + " и statusModerate = "
                            + statusModerate);
        }
        GroupFakeInfo groupFakeInfo =
                new UserSteps(connectionConfig, requestHeader).groupFakeSteps().getGroupParams(groupID);
        int shard =
                new UserSteps(connectionConfig, requestHeader).clientFakeSteps().getUserShard(groupFakeInfo.getLogin());
        List<BidsRecord> keywords = select(getDirectJooqDbSteps().useShard(shard).bidsSteps().getBidsByPid(groupID),
                having(on(BidsRecord.class).getId(), equalTo(keywordID)));
        assumeThat("получили ключевое слово из БД", keywords, hasSize(1));
        keywords.get(0).setIsSuspended(isSuspended);
        keywords.get(0).setStatusmoderate(statusModerate);
        getDirectJooqDbSteps().useShard(shard).bidsSteps().updateBids(keywords.get(0));
    }

    @Step("Keyword: фейково установить ключевому слову статус модерации (id = {0}, status = {1})")
    public void setKeywordStatus(long groupID, long keywordID, StatusEnum keywordStatus) {
        BidsStatusmoderate statusModerate;
        switch (keywordStatus) {
            case DRAFT:
                statusModerate = BidsStatusmoderate.New;
                break;
            case ACCEPTED:
                statusModerate = BidsStatusmoderate.Yes;
                break;
            case REJECTED:
                statusModerate = BidsStatusmoderate.No;
                break;
            default:
                throw new DirectAPIException(
                        "Неподдерживаемый тип статуса для фейковой установки ключевому слову - " + keywordStatus);
        }
        GroupFakeInfo groupFakeInfo =
                new UserSteps(connectionConfig, requestHeader).groupFakeSteps().getGroupParams(groupID);
        int shard =
                new UserSteps(connectionConfig, requestHeader).clientFakeSteps().getUserShard(groupFakeInfo.getLogin());
        List<BidsRecord> keywords = select(getDirectJooqDbSteps().useShard(shard).bidsSteps().getBidsByPid(groupID),
                having(on(BidsRecord.class).getId(), equalTo(keywordID)));
        assumeThat("получили ключевое слово из БД", keywords, hasSize(1));

        keywords.get(0).setStatusmoderate(statusModerate);
        getDirectJooqDbSteps().useShard(shard).bidsSteps().updateBids(keywords.get(0));
    }

    @Step("Keyword: дождаться установки PhraseID")
    public void waitForPhraseId(int shard, long keywordID) {
        ConditionFactories.PHRASE_ID_IS_SET.until(phraseIdIsSet(shard, keywordID));
    }

    private Callable<Boolean> phraseIdIsSet(int shard, long keywordID) {
        return () -> {
            BidsRecord keyword = getDirectJooqDbSteps().useShard(shard).bidsSteps().getBidById(keywordID);
            assumeThat("получили ключевое слово из БД", keyword, notNullValue());
            return !keyword.getPhraseid().equals(BigInteger.ZERO);
        };
    }

    public static Set<AutotargetingCategoriesEnum> getAutotargetingCategoriesEnumFromDB(String relevanceMatchCategories) {
        return StringUtils.isBlank(relevanceMatchCategories) ? Collections.emptySet()
                : Arrays.stream(relevanceMatchCategories.split("\\s*,\\s*"))
                .map(RELEVANCE_MATCH_CATEGORIES_TO_AUTOTARGETING_CATEGORIES::get)
                .collect(Collectors.toSet());
    }
}
