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

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.yandex.direct.api.v5.campaignsext.AddResponse;
import com.yandex.direct.api.v5.campaignsext.ArchiveResponse;
import com.yandex.direct.api.v5.campaignsext.CampaignFieldEnum;
import com.yandex.direct.api.v5.campaignsext.CampaignFundsEnum;
import com.yandex.direct.api.v5.campaignsext.CampaignGetItem;
import com.yandex.direct.api.v5.campaignsext.CampaignStateEnum;
import com.yandex.direct.api.v5.campaignsext.CampaignStateGetEnum;
import com.yandex.direct.api.v5.campaignsext.ContentPromotionCampaignSettingsEnum;
import com.yandex.direct.api.v5.campaignsext.DeleteResponse;
import com.yandex.direct.api.v5.campaignsext.DynamicTextCampaignSettingsEnum;
import com.yandex.direct.api.v5.campaignsext.GetRequest;
import com.yandex.direct.api.v5.campaignsext.GetResponse;
import com.yandex.direct.api.v5.campaignsext.MobileAppCampaignSettingsEnum;
import com.yandex.direct.api.v5.campaignsext.ResumeResponse;
import com.yandex.direct.api.v5.campaignsext.SuspendResponse;
import com.yandex.direct.api.v5.campaignsext.TextCampaignSettingsEnum;
import com.yandex.direct.api.v5.campaignsext.UnarchiveResponse;
import com.yandex.direct.api.v5.campaignsext.UpdateRequest;
import com.yandex.direct.api.v5.campaignsext.UpdateResponse;
import com.yandex.direct.api.v5.general.ActionResult;
import com.yandex.direct.api.v5.general.StatusEnum;
import com.yandex.direct.api.v5.general.YesNoEnum;
import org.apache.commons.beanutils.BeanMap;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import ru.yandex.autotests.direct.db.steps.DirectJooqDbSteps;
import ru.yandex.autotests.direct.utils.config.DirectTestRunProperties;
import ru.yandex.autotests.direct.utils.money.Currency;
import ru.yandex.autotests.direct.utils.money.Money;
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.errors.RbacErrorException;
import ru.yandex.autotests.directapi.apiclient.version5.ServiceNames;
import ru.yandex.autotests.directapi.darkside.steps.DarkSideSteps;
import ru.yandex.autotests.directapi.darkside.steps.fakesteps.CampaignFakeSteps;
import ru.yandex.autotests.directapi.enums.CampaignType;
import ru.yandex.autotests.directapi.exceptions.DirectAPIException;
import ru.yandex.autotests.directapi.model.api5.Action;
import ru.yandex.autotests.directapi.model.api5.campaignsext.AddRequestMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.ArchiveRequestMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.CampaignAddItemMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.CampaignUpdateItemMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.CampaignsSelectionCriteriaMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.ContentPromotionCampaignAddItemMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.ContentPromotionCampaignNetworkStrategyAddMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.ContentPromotionCampaignSearchStrategyAddMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.ContentPromotionCampaignSettingMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.ContentPromotionCampaignStrategyAddMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.DeleteRequestMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.DynamicTextCampaignAddItemMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.DynamicTextCampaignNetworkStrategyAddMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.DynamicTextCampaignSearchStrategyAddMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.DynamicTextCampaignSettingMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.DynamicTextCampaignStrategyAddMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.GetRequestMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.MobileAppCampaignAddItemMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.MobileAppCampaignNetworkStrategyAddMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.MobileAppCampaignSearchStrategyAddMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.MobileAppCampaignSettingMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.MobileAppCampaignStrategyAddMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.ResumeRequestMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.SuspendRequestMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.TextCampaignAddItemMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.TextCampaignNetworkStrategyAddMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.TextCampaignNetworkStrategyMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.TextCampaignSearchStrategyAddMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.TextCampaignSearchStrategyMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.TextCampaignSettingMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.TextCampaignStrategyAddMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.TextCampaignStrategyMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.TextCampaignUpdateItemMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.UnarchiveRequestMap;
import ru.yandex.autotests.directapi.model.api5.campaignsext.UpdateRequestMap;
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.JavaOrPerlExpectedResult;
import ru.yandex.autotests.directapi.rules.Api5Bin;
import ru.yandex.autotests.directapi.rules.Api5Binable;
import ru.yandex.autotests.directapi.steps.BaseApiSteps;
import ru.yandex.autotests.directapi.steps.UserSteps;
import ru.yandex.autotests.irt.testutils.allure.AssumptionException;
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.filter;
import static ch.lambdaj.Lambda.having;
import static ch.lambdaj.Lambda.on;
import static ch.lambdaj.Lambda.select;
import static org.hamcrest.Matchers.emptyIterable;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static ru.yandex.autotests.irt.testutils.allure.TestSteps.assertThat;
import static ru.yandex.autotests.irt.testutils.allure.TestSteps.assumeThat;

/**
 * Created by ginger on 22.07.15.
 */
public class CampExtSteps extends BaseApiSteps implements Api5Binable<Long> {
    public static final Random RANDOM = new Random();
    private LogSteps log = LogSteps.getLogger(this.getClass());
    private final DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.0");

    private static CampExtSteps _instance;

    public Api5Bin<Long> bin = new Api5Bin<>(this);

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

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

    public Callable<Boolean> campaingIsOn(final Long campaignId) {
        return () -> campaignsGet(
                new GetRequestMap()
                        .withFieldNames(CampaignFieldEnum.STATE)
                        .withSelectionCriteria(new CampaignsSelectionCriteriaMap().withIds(campaignId)))
                .getCampaigns().get(0).getState().equals(CampaignStateGetEnum.ON);
    }


    public Callable<StatusEnum> campaignStatusIs(String login, final Long campaignId) {
        return () -> campaignsGet(
                new GetRequestMap()
                        .withFieldNames(CampaignFieldEnum.STATUS)
                        .withSelectionCriteria(new CampaignsSelectionCriteriaMap().withIds(campaignId)),
                login)
                .getCampaigns().get(0).getStatus();
    }

    public List<CampaignGetItem> getOnCampaigns(String login) {
        List<CampaignGetItem> campaigns = getCampaigns(login,
                new GetRequestMap()
                        .withSelectionCriteria(new CampaignsSelectionCriteriaMap())
                        .withAllFieldNames());
        return campaigns.stream().filter(campaign -> campaign.getState() == CampaignStateGetEnum.ON)
                .collect(Collectors.toList());
    }

    public List<CampaignGetItem> getAcceptedCampaigns(String login) {
        List<CampaignGetItem> campaigns = getCampaigns(login,
                new GetRequestMap()
                        .withSelectionCriteria(new CampaignsSelectionCriteriaMap())
                        .withAllFieldNames());
        return campaigns.stream().filter(campaign -> campaign.getStatus() == StatusEnum.ACCEPTED)
                .collect(Collectors.toList());
    }

    public Callable<Boolean> hasOnCampaign(final String login) {
        return () -> getOnCampaigns(login).size() > 0;
    }

    public Callable<Boolean> campaignFundsChanged(String login, CampaignGetItem campaignBefore) {
        return campaignFundsChanged(login, campaignBefore, properties.isDirectUseRealNotifyOrder());
    }

    public Callable<Boolean> campaignFundsChanged(CampaignGetItem campaignBefore) {
        return campaignFundsChanged(null, campaignBefore, properties.isDirectUseRealNotifyOrder());
    }

    public Callable<Boolean> campaignFundsChanged(CampaignGetItem campaignBefore, boolean billingNotifications) {
        return campaignFundsChanged(null, campaignBefore, billingNotifications);
    }

    public Callable<Boolean> campaignFundsChanged(
            String login, CampaignGetItem campaignBefore, boolean billingNotifications)
    {
        return () -> {
            if (campaignBefore.getFunds().getMode() == CampaignFundsEnum.CAMPAIGN_FUNDS) {
                Long sumBefore = campaignBefore.getFunds().getCampaignFunds().getSum();
                CampaignGetItem campaignAfter = campaignsGet(login, campaignBefore.getId(),
                        CampaignFieldEnum.FUNDS, CampaignFieldEnum.ID);
                Long sumAfter = campaignAfter.getFunds().getCampaignFunds().getSum();
                if (!billingNotifications) {
                    UserSteps.getInstance().balanceSteps().synchronizeWithBalance(campaignAfter.getId());
                }
                return
                        !sumBefore.equals(sumAfter);
            } else {
                log.info("Кампания подключена к ОС");
                return false;
            }
        };
    }

    public Callable<Boolean> campaignFundsChanged(long campaignId) {
        return campaignFundsChanged(null, campaignId, properties.isDirectUseRealNotifyOrder());
    }

    public Callable<Boolean> campaignFundsChanged(String login, long campaignId) {
        return campaignFundsChanged(login, campaignId, properties.isDirectUseRealNotifyOrder());
    }

    public Callable<Boolean> campaignFundsChanged(long campaignId, boolean waitForRealNotification) {
        return campaignFundsChanged(null, campaignId, waitForRealNotification);
    }

    public Callable<Boolean> campaignFundsChanged(String login, long campaignId, boolean waitForRealNotification) {
        return () -> campaignFundsSumIs(login, campaignId, waitForRealNotification).call().floatValue()
                .compareTo(0.0f) > 0 ;
    }

    public Callable<Long> campaignFundsSumLongIs(long campaignId) {
        return campaignFundsSumLongIs(null, campaignId);
    }

    public Callable<Long> campaignFundsSumLongIs(String login, long campaignId) {
        return () -> campaignFundsSumIs(login, campaignId, properties.isDirectUseRealNotifyOrder())
                .call().bidLong().longValue();
    }

    public Callable<Money> campaignFundsSumIs(long campaignId) {
        return campaignFundsSumIs(null, campaignId, properties.isDirectUseRealNotifyOrder());
    }

    public Callable<Money> campaignFundsSumIs(String login, long campaignId, boolean waitForRealNotification) {
        return () -> {
            // синхронизируемся с балансом перед получением суммы, что бы ускорить тесты
            if (!waitForRealNotification) {
                UserSteps.getInstance().balanceSteps().synchronizeWithBalance(campaignId);
            }
            Money sum = getCampaignSum(login, campaignId).setScale(2, RoundingMode.HALF_UP);
            log.info(String.format("Текущая сумма средств на кампании %d: %s", campaignId, sum.toString()));
            return sum;
        };
    }

    public void removeCampaignsFromQueue(List<Long> campaignIds) {
        log.info("Удаляем кампании из очереди TRASHER'а");
        for (Long id : campaignIds) {
            bin.removeFromBin(id);
        }
    }

    public void putCampaignToQueue(String login, List<Long> campaignIds) {
        log.info("Добавляем кампании в очередь на удаление " + campaignIds.toString());
        for (Long id : campaignIds) {
            bin.throwToBin(id, login != null ? login :
                    (requestHeader.getClientLogin() != null ? requestHeader.getClientLogin() :
                            (requestHeader.getFakeLogin() != null ? requestHeader.getFakeLogin()
                                    : requestHeader.getLogin())));
        }
    }

    public void putCampaignToQueue(String login, Long campaignID) {
        putCampaignToQueue(login, Arrays.asList(campaignID));
    }

    @Override
    public Callable clearBin(final Map<Long, String> binData) {
        return new Callable() {
            @Override
            public Object call() throws Exception {
                Map<String, List<Long>> loginToIds = new HashMap<>();
                for (Map.Entry<Long, String> entry : binData.entrySet()) {
                    Long id = entry.getKey();
                    String login = entry.getValue();
                    if (!loginToIds.containsKey(login)) {
                        loginToIds.put(login, new ArrayList<Long>());
                    }
                    loginToIds.get(login).add(id);
                }

                DarkSideSteps darkSideSteps = getDarkSideSteps();
                CampaignFakeSteps campaignFakeSteps = darkSideSteps.getCampaignFakeSteps();
                campaignFakeSteps.makeCampaignReadyForDelete(binData.keySet());

                //удалим кампанию из транспортных очередей
                if (DirectTestRunProperties.getInstance().isDirectTransportClearQueues()) {
                    DirectJooqDbSteps jooqDbSteps = darkSideSteps.getDirectJooqDbSteps();
                    for (Long campaignID : binData.keySet()) {
                        try {
                            int shard = jooqDbSteps.shardingSteps().getShardByCid(campaignID);
                            jooqDbSteps.useShard(shard).bsResyncQueueSteps()
                                    .deleteCampaignFromBsResyncQueueByCid(campaignID);
                            jooqDbSteps.bsExportQueueSteps().deleteBsExportQueue(campaignID);
                        } catch (Exception e) {
                            log.info(
                                    "TRASHER: ошибка удаления кампании API5, ошибка при удалении из очередей транспорта. "
                                            + e);
                        }
                    }
                }

                String login;
                List<Long> ids;
                for (Map.Entry<String, List<Long>> entry : loginToIds.entrySet()) {
                    login = entry.getKey();
                    ids = entry.getValue();
                    try {
                        campaignsDelete(login, ids.toArray(new Long[0]));
                    } catch (Exception e) {
                        log.info("TRASHER: ошибка удаления кампании в API5." + e);
                    } catch (AssertionError error) {
                        log.info("TRASHER: ошибка при удалении кампании API5. Возможно, кампания была удалена ранее.");
                    }
                }
                return null;
            }
        };
    }

    //region Get
    @Step("[Campaigns]: Get")
    public GetResponse campaignsGet(GetRequestMap parameters, String login) {
        return defaultClientV5()
                .invokeMethod(ServiceNames.CAMPAIGNS_EXT, login, Action.GET, (GetRequest) parameters.getBean());
    }

    public GetResponse campaignsGet(GetRequestMap parameters) {
        return campaignsGet(parameters, null);
    }

    public List<CampaignGetItem> getCampaigns(String login, GetRequestMap request) {
        return campaignsGet(request, login).getCampaigns();
    }

    public List<CampaignGetItem> getCampaigns(GetRequestMap request) {
        return getCampaigns(null, request);
    }

    @Step
    public List<CampaignGetItem> getCampaigns(String login, Long... campaignIds) {
        GetResponse getResponse = campaignsGet(
                new GetRequestMap()
                        .withSelectionCriteria(
                                new CampaignsSelectionCriteriaMap()
                                        .withIds(campaignIds))
                        .withAllFieldNames()
                        .withAllTextCampaignFieldNames(),
                login);
        return getResponse.getCampaigns();
    }

    @Step
    public List<CampaignGetItem> getCampaigns(Long... campaignIds) {
        return getCampaigns(null, campaignIds);
    }

    /**
     * Получить информацию о кампании по id. Запрашиваются все поля для текстовых кампаний.
     */
    public CampaignGetItem getCampaign(long campaignId) {
        return getCampaign(null, campaignId);
    }

    /**
     * Получить информацию о кампании по id. Запрашиваются все поля для текстовых кампаний.
     *
     * @param login Client-Login запроса
     */
    public CampaignGetItem getCampaign(String login, long campaignId) {
        List<CampaignGetItem> campaigns = getCampaigns(login, campaignId);
        assumeThat("Получена одна кампания", campaigns, hasSize(1));
        return campaigns.get(0);
    }

    /**
     * Получить кол-во средств на кампании.
     * <p>
     * Выкидывает {@link AssumptionException} в случае, если подключен общий счёт
     *
     * @param login      Client-Login запроса
     * @param campaignId идентификатор кампании, кол-во средств на которой нужно получить
     * @return кол-во средств в валюте кампании
     */
    public Money getCampaignSum(String login, long campaignId) {
        CampaignGetItem getItem = campaignsGet(login, campaignId, CampaignFieldEnum.FUNDS, CampaignFieldEnum.CURRENCY);
        assumeThat("Кампания не должна быть подключена к ОС",
                getItem.getFunds().getMode(), not(equalTo(CampaignFundsEnum.SHARED_ACCOUNT_FUNDS)));
        Currency currency = Currency.getFor(getItem.getCurrency().value());
        return Money.valueOf(getItem.getFunds().getCampaignFunds().getSum(), currency)
                .subtractVAT()
                .divide(BigDecimal.valueOf(1_000_000));
    }

    public List<Long> getAllCampaignIds(String login) {
        GetResponse getResponse = campaignsGet(
                new GetRequestMap()
                        .withSelectionCriteria(
                                new CampaignsSelectionCriteriaMap())
                        .withFieldNames(CampaignFieldEnum.ID),
                login);
        return getResponse.getCampaigns().stream().map(CampaignGetItem::getId).collect(Collectors.toList());
    }

    public List<Long> getAllCampaignIds() {
        return getAllCampaignIds(null);
    }

    public List<Long> getNotConvertedCampaignIds(String login) {
        GetResponse getResponse = campaignsGet(
                new GetRequestMap()
                        .withSelectionCriteria(
                                new CampaignsSelectionCriteriaMap()
                                        .withStates(Stream.of(CampaignStateEnum.values())
                                                .filter(s -> !s.equals(CampaignStateEnum.CONVERTED))
                                                .toArray(CampaignStateEnum[]::new)))
                        .withFieldNames(CampaignFieldEnum.ID),
                login);
        return getResponse.getCampaigns().stream().map(CampaignGetItem::getId).collect(Collectors.toList());
    }

    public List<Long> getNotConvertedCampaignIds() {
        return getNotConvertedCampaignIds(null);
    }

    @Step("[Campaigns]: Get: получить поля кампании по ID")
    public CampaignGetItem campaignsGet(String login, Long id, CampaignFieldEnum... fieldNames) {
        GetResponse getResponse = campaignsGet(
                new GetRequestMap()
                        .withSelectionCriteria(
                                new CampaignsSelectionCriteriaMap()
                                        .withIds(id)
                        )
                        .withFieldNames(fieldNames),
                login);
        assumeThat("вернулась 1 кампания", getResponse.getCampaigns(), hasSize(1));
        return getResponse.getCampaigns().get(0);
    }

    public CampaignGetItem campaignsGet(Long id, CampaignFieldEnum... fieldNames) {
        return campaignsGet(null, id, fieldNames);
    }


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

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

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

    public void expectErrorOnCampaignsGet(Object parameters, Api5Error api5Error) {
        shouldGetJSONErrorOn(ServiceNames.CAMPAIGNS_EXT, null, Action.GET, parameters, api5Error);
    }
    //endregion

    //region Add
    @Step
    public AddResponse campaignsAdd(AddRequestMap parameters, String login) {
        return campaignsAdd(parameters.getBean(), login);
    }

    @Step("[Campaigns]: Add")
    public AddResponse campaignsAdd(Object params, String login) {
        AddResponse response = defaultClientV5().invokeMethod(ServiceNames.CAMPAIGNS_EXT, login, Action.ADD, params);
        List<Long> ids = extractIdsFromAddResponse(response);
        putCampaignToQueue(login, ids);
        return response;
    }

    public AddResponse campaignsAdd(AddRequestMap parameters) {
        return campaignsAdd(parameters, null);
    }

    public List<Long> campaignsAddWithCheck(String login, AddRequestMap request) {
        List<ActionResult> results = campaignsAdd(request, login).getAddResults();
        lookForResponseExceptions(results);
        return getIDsFromActionResults(results);
    }

    public List<Long> campaignsAddWithCheck(AddRequestMap request) {
        return campaignsAddWithCheck(null, request);
    }

    public List<Long> campaignsAddWithCheck(String login, CampaignAddItemMap... campaigns) {
        AddRequestMap request = new AddRequestMap().withCampaigns(campaigns);
        return campaignsAddWithCheck(login, request);
    }

    public List<Long> campaignsAddWithCheck(CampaignAddItemMap... campaigns) {
        return campaignsAddWithCheck(null, campaigns);
    }

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

    public AddResponse shouldGetResultOnAdd(AddRequestMap parameters, ExpectedResult... expectedResults) {
        return shouldGetResultOnAdd(parameters, null, expectedResults);
    }

    public AddResponse shouldGetResultOnAdd(AddRequestMap parameters, JavaOrPerlExpectedResult... expectedResults) {
        return (AddResponse) shouldGetResultOn(ServiceNames.CAMPAIGNS_EXT, null, Action.ADD, parameters, expectedResults);
    }

    public AddResponse shouldGetResultOnAdd(Object parameters, ExpectedResult... expectedResults) {
        return shouldGetResultOnAdd(parameters, null, expectedResults);
    }

    private AddResponse shouldGetResultOnAdd(Object parameters, String login, ExpectedResult[] expectedResults) {
        return (AddResponse) shouldGetResultOn(login, Action.ADD, parameters, expectedResults);
    }

    public void expectErrorOnCampaignsAdd(AddRequestMap parameters, String login, Api5Error api5Error) {
        shouldGetErrorOn(ServiceNames.CAMPAIGNS_EXT, login, Action.ADD, parameters, api5Error);
    }

    public void expectErrorOnCampaignsAdd(AddRequestMap parameters, Api5Error api5Error) {
        expectErrorOnCampaignsAdd(parameters, null, api5Error);
    }

    public void expectErrorOnCampaignsAdd(Object parameters, String login, Api5Error api5Error) {
        shouldGetJSONErrorOn(ServiceNames.CAMPAIGNS_EXT, login, Action.ADD, parameters, api5Error);
    }

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

    public Long addCampaign(CampaignAddItemMap campaignAddItemMap, String login) {
        AddResponse response = campaignsAdd(new AddRequestMap().withCampaigns(campaignAddItemMap), login);
        List<Long> ids = extractIdsFromAddResponse(response);
        assumeThat("создана одна кампания", ids, hasSize(1));
        log.info("Кампания создана: campaignID = " + ids.get(0));
        return ids.get(0);
    }

    public Long addCampaign(CampaignAddItemMap campaignAddItemMap) {
        return addCampaign(campaignAddItemMap, null);
    }

    public Long addCampaign(Object params, String login) {
        AddResponse response = campaignsAdd(params, login);
        List<Long> ids = extractIdsFromAddResponse(response);
        assumeThat("создана одна кампания", ids, hasSize(1));
        log.info("Кампания создана: campaignID = " + ids.get(0));
        return ids.get(0);
    }

    public Long addCampaign(Object params) {
        return addCampaign(params, null);
    }

    public Long addDefaultTextCampaign() {
        return addDefaultTextCampaign(null);
    }

    public Long addDefaultTextCampaign(String login) {
        return retry(() ->
                addCampaign(new CampaignAddItemMap().defaultCampaignAddItem().withDefaultTextCampaign(), login));
    }

    private <T> T retry(Supplier<T> action) {
        for (int i = 0; i < 5; i++) {
            try {
                return action.get();
            } catch (RbacErrorException ree) {
                log.info(ree);
                try {
                    long sleepIntervalMillis = 60000L + RANDOM.nextInt(240000);
                    log.info(String.format("sleeping for %s ms", sleepIntervalMillis));
                    Thread.sleep(sleepIntervalMillis);
                } catch (InterruptedException e) {
                    throw new DirectAPIException("interrupted", e);
                }
            }
        }
        throw new DirectAPIException("failed to add campaign");
    }

    public Long addDefaultTextCampaignWithSearchStrategy(TextCampaignSearchStrategyAddMap searchStrategy,
                                                         String login)
    {
        return addCampaign(new CampaignAddItemMap()
                .defaultCampaignAddItem()
                .withTextCampaign(
                        new TextCampaignAddItemMap()
                                .withBiddingStrategy(
                                        new TextCampaignStrategyAddMap()
                                                .withSearch(searchStrategy)
                                                .withNetwork(new TextCampaignNetworkStrategyAddMap()
                                                        .defaultMaximumCoverage())
                                )
                ), login);
    }

    public Long addDefaultTextCampaignWithSearchStrategy(TextCampaignSearchStrategyAddMap searchStrategy) {
        return addDefaultTextCampaignWithSearchStrategy(searchStrategy, null);
    }

    public Long addDefaultTextCampaignWithNetworkStrategy(TextCampaignNetworkStrategyAddMap networkStrategy,
                                                          String login)
    {
        return addCampaign(new CampaignAddItemMap()
                .defaultCampaignAddItem()
                .withTextCampaign(
                        new TextCampaignAddItemMap()
                                .withBiddingStrategy(
                                        new TextCampaignStrategyAddMap()
                                                .withSearch(
                                                        new TextCampaignSearchStrategyAddMap().defaultHighestPosition())
                                                .withNetwork(networkStrategy)
                                )
                ), login);
    }

    public Long addDefaultTextCampaignWithNetworkStrategy(TextCampaignNetworkStrategyAddMap networkStrategy) {
        return addDefaultTextCampaignWithNetworkStrategy(networkStrategy, null);
    }

    public Long addDefaultTextCampaignWithStrategies(TextCampaignSearchStrategyAddMap searchStrategy,
                                                     TextCampaignNetworkStrategyAddMap networkStrategy,
                                                     String login)
    {
        return addCampaign(new CampaignAddItemMap()
                .defaultCampaignAddItem()
                .withTextCampaign(
                        new TextCampaignAddItemMap()
                                .withBiddingStrategy(
                                        new TextCampaignStrategyAddMap()
                                                .withSearch(searchStrategy)
                                                .withNetwork(networkStrategy)
                                )
                ), login);
    }

    public Long addDefaultTextCampaignWithStrategies(TextCampaignSearchStrategyAddMap searchStrategy,
                                                     TextCampaignNetworkStrategyAddMap networkStrategy)
    {
        return addDefaultTextCampaignWithStrategies(searchStrategy, networkStrategy, null);
    }

    public Long addDefaultTextCampaignWithIndependentStrategies() {
        return addDefaultTextCampaignWithIndependentStrategies(null);
    }

    public Long addDefaultTextCampaignWithIndependentStrategies(String login) {
        return addDefaultTextCampaignWithStrategies(
                new TextCampaignSearchStrategyAddMap().defaultHighestPosition(),
                new TextCampaignNetworkStrategyAddMap().defaultMaximumCoverage(),
                login
        );
    }

    @Step("[Campaigns]: Add с возможностью задать настроку обслуживания менеджером")
    public Long addDefaultTextCampaignWithRequireServicing(YesNoEnum settingValue, String login) {
        return addCampaign(
                new CampaignAddItemMap()
                        .defaultCampaignAddItem()
                        .withTextCampaign(new TextCampaignAddItemMap()
                                .defaultTextCampaign()
                                .withSettings(new TextCampaignSettingMap()
                                        .withOption(TextCampaignSettingsEnum.REQUIRE_SERVICING)
                                        .withValue(settingValue))),
                login
        );
    }

    @Step("[Campaigns]: Add с возможностью задать настроку обслуживания менеджером")
    public Long addDefaultTextCampaignWithRequireServicing(YesNoEnum settingValue) {
        return addDefaultTextCampaignWithRequireServicing(settingValue, null);
    }

    public Long addDefaultDynamicTextCampaignWithSearchStrategy(DynamicTextCampaignSearchStrategyAddMap searchStrategy,
                                                                String login)
    {
        return addCampaign(new CampaignAddItemMap()
                .defaultCampaignAddItem()
                .withDynamicTextCampaign(
                        new DynamicTextCampaignAddItemMap()
                                .withBiddingStrategy(
                                        new DynamicTextCampaignStrategyAddMap()
                                                .withSearch(searchStrategy)
                                                .withNetwork(new DynamicTextCampaignNetworkStrategyAddMap()
                                                        .defaultServingOff())
                                )
                ), login);
    }


    public Long addDefaultDynamicTextCampaignWithSearchStrategy(
            DynamicTextCampaignSearchStrategyAddMap searchStrategy)
    {
        return addDefaultDynamicTextCampaignWithSearchStrategy(searchStrategy, null);
    }

    public Long addDefaultDynamicTextCampaignWithNetworkStrategy(
            DynamicTextCampaignNetworkStrategyAddMap networkStrategy, String login)
    {
        return addCampaign(new CampaignAddItemMap()
                .defaultCampaignAddItem()
                .withDynamicTextCampaign(
                        new DynamicTextCampaignAddItemMap()
                                .withBiddingStrategy(
                                        new DynamicTextCampaignStrategyAddMap()
                                                .withSearch(new DynamicTextCampaignSearchStrategyAddMap()
                                                        .defaultHighestPosition())
                                                .withNetwork(networkStrategy)
                                )
                ), login);
    }

    public Long addDefaultDynamicTextCampaignWithNetworkStrategy(
            DynamicTextCampaignNetworkStrategyAddMap networkStrategy)
    {
        return addDefaultDynamicTextCampaignWithNetworkStrategy(networkStrategy, null);
    }

    public Long addDefaultDynamicTextCampaignWithStrategies(DynamicTextCampaignSearchStrategyAddMap searchStrategy,
                                                            DynamicTextCampaignNetworkStrategyAddMap networkStrategy,
                                                            String login)
    {
        return addCampaign(new CampaignAddItemMap()
                .defaultCampaignAddItem()
                .withDynamicTextCampaign(
                        new DynamicTextCampaignAddItemMap()
                                .withBiddingStrategy(
                                        new DynamicTextCampaignStrategyAddMap()
                                                .withSearch(searchStrategy)
                                                .withNetwork(networkStrategy)
                                )
                ), login);
    }

    public Long addDefaultDynamicTextCampaignWithStrategies(DynamicTextCampaignSearchStrategyAddMap searchStrategy,
                                                            DynamicTextCampaignNetworkStrategyAddMap networkStrategy)
    {
        return addDefaultDynamicTextCampaignWithStrategies(searchStrategy, networkStrategy, null);
    }


    @Step("[Campaigns]: Add с возможностью задать настроку обслуживания менеджером")
    public Long addDefaultDynamicTextCampaignWithRequireServicing(YesNoEnum settingValue, String login) {
        return addCampaign(
                new CampaignAddItemMap()
                        .defaultCampaignAddItem()
                        .withDynamicTextCampaign(new DynamicTextCampaignAddItemMap()
                                .defaultDynamicTextCampaign()
                                .withSettings(new DynamicTextCampaignSettingMap()
                                        .withOption(DynamicTextCampaignSettingsEnum.REQUIRE_SERVICING)
                                        .withValue(settingValue))),
                login
        );
    }

    @Step("[Campaigns]: Add с возможностью задать настроку обслуживания менеджером")
    public Long addDefaultDynamicTextCampaignWithRequireServicing(YesNoEnum settingValue) {
        return addDefaultDynamicTextCampaignWithRequireServicing(settingValue, null);
    }

    public Long addDefaultContentPromotionCampaignWithSearchStrategy(ContentPromotionCampaignSearchStrategyAddMap
                                                                             searchStrategy,
                                                                     String login)
    {
        return addCampaign(new CampaignAddItemMap()
                .defaultCampaignAddItem()
                .withContentPromotionCampaign(
                        new ContentPromotionCampaignAddItemMap()
                                .withBiddingStrategy(
                                        new ContentPromotionCampaignStrategyAddMap()
                                                .withSearch(searchStrategy)
                                                .withNetwork(new ContentPromotionCampaignNetworkStrategyAddMap()
                                                        .defaultServingOff())
                                )
                ), login);
    }


    public Long addDefaultContentPromotionCampaignWithSearchStrategy(
            ContentPromotionCampaignSearchStrategyAddMap searchStrategy)
    {
        return addDefaultContentPromotionCampaignWithSearchStrategy(searchStrategy, null);
    }

    public Long addDefaultContentPromotionCampaignWithNetworkStrategy(
            ContentPromotionCampaignNetworkStrategyAddMap networkStrategy, String login)
    {
        return addCampaign(new CampaignAddItemMap()
                .defaultCampaignAddItem()
                .withContentPromotionCampaign(
                        new ContentPromotionCampaignAddItemMap()
                                .withBiddingStrategy(
                                        new ContentPromotionCampaignStrategyAddMap()
                                                .withSearch(new ContentPromotionCampaignSearchStrategyAddMap()
                                                        .defaultHighestPosition())
                                                .withNetwork(networkStrategy)
                                )
                ), login);
    }

    public Long addDefaultContentPromotionCampaignWithNetworkStrategy(
            ContentPromotionCampaignNetworkStrategyAddMap networkStrategy)
    {
        return addDefaultContentPromotionCampaignWithNetworkStrategy(networkStrategy, null);
    }

    public Long addDefaultContentPromotionCampaignWithStrategies(ContentPromotionCampaignSearchStrategyAddMap
                                                                         searchStrategy,
                                                                 ContentPromotionCampaignNetworkStrategyAddMap
                                                                         networkStrategy,
                                                                 String login)
    {
        return addCampaign(new CampaignAddItemMap()
                .defaultCampaignAddItem()
                .withContentPromotionCampaign(
                        new ContentPromotionCampaignAddItemMap()
                                .withBiddingStrategy(
                                        new ContentPromotionCampaignStrategyAddMap()
                                                .withSearch(searchStrategy)
                                                .withNetwork(networkStrategy)
                                )
                ), login);
    }

    public Long addDefaultContentPromotionCampaignWithStrategies(ContentPromotionCampaignSearchStrategyAddMap
                                                                         searchStrategy,
                                                                ContentPromotionCampaignNetworkStrategyAddMap
                                                                        networkStrategy)
    {
        return addDefaultContentPromotionCampaignWithStrategies(searchStrategy, networkStrategy, null);
    }


    @Step("[Campaigns]: Add с возможностью задать настроку обслуживания менеджером")
    public Long addContentPromotionCampaignWithRequireServicing(YesNoEnum settingValue, String login) {
        return addCampaign(
                new CampaignAddItemMap()
                        .defaultCampaignAddItem()
                        .withContentPromotionCampaign(new ContentPromotionCampaignAddItemMap()
                                .defaultContentPromotionCampaign()
                                .withSettings(new ContentPromotionCampaignSettingMap()
                                        .withOption(ContentPromotionCampaignSettingsEnum.REQUIRE_SERVICING)
                                        .withValue(settingValue))),
                login
        );
    }

    @Step("[Campaigns]: Add с возможностью задать настроку обслуживания менеджером")
    public Long addContentPromotionCampaignWithRequireServicing(YesNoEnum settingValue) {
        return addContentPromotionCampaignWithRequireServicing(settingValue, null);
    }

    public Long addDefaultMobileAppCampaignWithStrategies(MobileAppCampaignSearchStrategyAddMap searchStrategy,
                                                          MobileAppCampaignNetworkStrategyAddMap networkStrategy, String login)
    {
        return addCampaign(
                new CampaignAddItemMap().defaultCampaignAddItem().withMobileAppCampaign(
                        new MobileAppCampaignAddItemMap().withBiddingStrategy(
                                new MobileAppCampaignStrategyAddMap()
                                        .withSearch(searchStrategy)
                                        .withNetwork(networkStrategy)
                        )
                ),
                login
        );
    }

    public Long addDefaultMobileAppCampaignWithStrategies(MobileAppCampaignSearchStrategyAddMap searchStrategy,
                                                          MobileAppCampaignNetworkStrategyAddMap networkStrategy)
    {
        return addDefaultMobileAppCampaignWithStrategies(searchStrategy, networkStrategy, null);
    }

    public Long addDefaultMobileAppCampaign() {
        return addDefaultMobileAppCampaign(null);
    }

    public Long addDefaultMobileAppCampaign(String login) {
        return addCampaign(new CampaignAddItemMap().defaultCampaignAddItem().withDefaultMobileAppCampaign(), login);
    }


    @Step("[Campaigns]: Add с возможностью задать настроку обслуживания менеджером")
    public Long addDefaultMobileAppCampaignWithRequireServicing(YesNoEnum settingValue, String login) {
        return addCampaign(
                new CampaignAddItemMap()
                        .defaultCampaignAddItem()
                        .withMobileAppCampaign(new MobileAppCampaignAddItemMap()
                                .defaultMobileAppCampaign()
                                .withSettings(new MobileAppCampaignSettingMap()
                                        .withOption(MobileAppCampaignSettingsEnum.REQUIRE_SERVICING)
                                        .withValue(settingValue))),
                login
        );
    }

    @Step("[Campaigns]: Add с возможностью задать настроку обслуживания менеджером")
    public Long addDefaultMobileAppCampaignWithRequireServicing(YesNoEnum settingValue) {
        return addDefaultMobileAppCampaignWithRequireServicing(settingValue, null);
    }

    public Long addDefaultDynamicTextCampaign() {
        return addDefaultDynamicTextCampaign(null);
    }

    public Long addDefaultDynamicTextCampaign(String login) {
        return addCampaign(new CampaignAddItemMap().defaultCampaignAddItem().withDefaultDynamicTextCampaign(), login);
    }

    public Long addDefaultCpmBannerCampaign() {
        return addDefaultCpmBannerCampaign(null);
    }

    public Long addDefaultCpmBannerCampaign(String login) {
        return addCampaign(new CampaignAddItemMap().defaultCampaignAddItem().withDefaultCpmBannerCampaign(), login);
    }

    public Long addDefaultContentPromotionCampaign() {
        return addDefaultContentPromotionCampaign(null);
    }

    public Long addDefaultContentPromotionCampaign(String login) {
        return addCampaign(new CampaignAddItemMap().defaultCampaignAddItem().withDefaultContentPromotionCampaign(),
                login);
    }

    public Long addDefaultCampaign(CampaignType type) {
        return addDefaultCampaign(null, type);
    }

    public Long addDefaultCampaign(String login, CampaignType type) {
        return addCampaign(new CampaignAddItemMap().defaultCampaignAddItem().withDefaultCampaign(type), login);
    }

    public List<Long> addDefaultTextCampaigns(String login, int amount) {
        CampaignAddItemMap[] campaignAddItemMaps = new CampaignAddItemMap[amount];
        Arrays.fill(campaignAddItemMaps, new CampaignAddItemMap()
                .defaultCampaignAddItem()
                .withDefaultTextCampaign());
        AddResponse response = campaignsAdd(new AddRequestMap().withCampaigns(campaignAddItemMaps), login);
        List<Long> ids = extractIdsFromAddResponse(response);
        putCampaignToQueue(login, ids);
        assumeThat(String.format("создано %d текстовых кампаний", amount), ids, hasSize(amount));
        return ids;
    }

    public Long addCampaignWithEnableSiteMonitoringSetting(YesNoEnum settingValue) {
        return addCampaign(new CampaignAddItemMap()
                .defaultCampaignAddItem()
                .withTextCampaign(new TextCampaignAddItemMap()
                        .defaultTextCampaign()
                        .withSettings(new TextCampaignSettingMap()
                                .withOption(TextCampaignSettingsEnum.ENABLE_SITE_MONITORING)
                                .withValue(settingValue))));
    }

    @Step("[Campaigns]: Update")
    public UpdateResponse campaignsUpdate(UpdateRequestMap parameters, String login) {
        return defaultClientV5()
                .invokeMethod(ServiceNames.CAMPAIGNS_EXT, login, Action.UPDATE, (UpdateRequest) parameters.getBean());
    }

    @Step
    public UpdateResponse campaignsUpdate(UpdateRequestMap parameters) {
        return campaignsUpdate(parameters, null);
    }

    public List<Long> campaignsUpdateWithCheck(String login, UpdateRequestMap request) {
        List<ActionResult> results = campaignsUpdate(request, login).getUpdateResults();
        lookForResponseExceptions(results);
        return getIDsFromActionResults(results);
    }

    public List<Long> campaignsUpdateWithCheck(UpdateRequestMap request) {
        return campaignsUpdateWithCheck(null, request);
    }

    public List<Long> campaignsUpdateWithCheck(String login, CampaignUpdateItemMap... campaigns) {
        UpdateRequestMap request = new UpdateRequestMap().withCampaigns(campaigns);
        return campaignsUpdateWithCheck(login, request);
    }

    public List<Long> campaignsUpdateWithCheck(CampaignUpdateItemMap... campaigns) {
        return campaignsUpdateWithCheck(null, campaigns);
    }

    public UpdateResponse shouldGetResultOnUpdate(
            UpdateRequestMap parameters, String login, ExpectedResult... expectedResults)
    {
        return (UpdateResponse) shouldGetResultOn(ServiceNames.CAMPAIGNS_EXT, login, Action.UPDATE, parameters,
                expectedResults);
    }

    public UpdateResponse shouldGetResultOnUpdate(
            UpdateRequestMap parameters, String login, JavaOrPerlExpectedResult... expectedResults)
    {
        return (UpdateResponse) shouldGetResultOn(ServiceNames.CAMPAIGNS_EXT, login, Action.UPDATE, parameters,
                expectedResults);
    }
    public UpdateResponse shouldGetResultOnUpdate(
            Object parameters, String login, ExpectedResult... expectedResults)
    {
        return (UpdateResponse) shouldGetResultOn(ServiceNames.CAMPAIGNS_EXT, login, Action.UPDATE, parameters,
                expectedResults);
    }

    public UpdateResponse shouldGetResultOnUpdate(UpdateRequestMap parameters, ExpectedResult... expectedResults) {
        return shouldGetResultOnUpdate(parameters, null, expectedResults);
    }

    public UpdateResponse shouldGetResultOnUpdate(UpdateRequestMap parameters, 
                                                  JavaOrPerlExpectedResult... expectedResults) {
        return shouldGetResultOnUpdate(parameters, null, expectedResults);
    }

    public UpdateResponse shouldGetResultOnUpdate(Object parameters, ExpectedResult... expectedResults) {
        return shouldGetResultOnUpdate(parameters, null, expectedResults);
    }

    public void expectErrorOnCampaignsUpdate(UpdateRequestMap parameters, String login, Api5Error api5Error) {
        shouldGetErrorOn(ServiceNames.CAMPAIGNS_EXT, login, Action.UPDATE, parameters, api5Error);
    }

    public void expectErrorOnCampaignsUpdate(UpdateRequestMap parameters, Api5Error api5Error) {
        expectErrorOnCampaignsUpdate(parameters, null, api5Error);
    }

    public void expectErrorOnCampaignsUpdate(Object parameters, String login, Api5Error api5Error) {
        shouldGetJSONErrorOn(ServiceNames.CAMPAIGNS_EXT, login, Action.UPDATE, parameters, api5Error);
    }

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

    public void setAutobudgetOnContextStrategyOnTextCampaign(long campaignId, Currency currency) {
        setAutobudgetOnContextStrategyOnTextCampaign(null, campaignId, currency);
    }

    public void setAutobudgetOnContextStrategyOnTextCampaign(String login, long campaignId, Currency currency) {
        campaignsUpdateWithCheck(
                login,
                new UpdateRequestMap().withCampaigns(
                        new CampaignUpdateItemMap()
                                .withId(campaignId)
                                .withTextCampaign(
                                        new TextCampaignUpdateItemMap().withBiddingStrategy(
                                                new TextCampaignStrategyMap()
                                                        .withSearch(new TextCampaignSearchStrategyMap()
                                                                .defaultServingOff())
                                                        .withNetwork(new TextCampaignNetworkStrategyMap()
                                                                .defaultAverageCpc(currency))
                                        ))));
    }
    @Step
    public void setAutobudgetOnSearchStrategyOnTextCampaign(long campaignId, Currency currency) {
        setAutobudgetOnSearchStrategyOnTextCampaign(null, campaignId, currency);
    }
    @Step
    public void setAutobudgetOnSearchStrategyOnTextCampaign(String login, long campaignId, Currency currency) {
        campaignsUpdateWithCheck(
                login,
                new UpdateRequestMap().withCampaigns(
                        new CampaignUpdateItemMap()
                                .withId(campaignId)
                                .withTextCampaign(
                                        new TextCampaignUpdateItemMap().withBiddingStrategy(
                                                new TextCampaignStrategyMap()
                                                        .withSearch(new TextCampaignSearchStrategyMap()
                                                                .defaultAverageCpc(currency))
                                                        .withNetwork(new TextCampaignNetworkStrategyMap()
                                                                .defaultServingOff())
                                        ))));
    }

    public void setManualOnSearchStrategyOnTextCampaign(long campaignId) {
        setManualOnSearchStrategyOnTextCampaign(null, campaignId);
    }

    public void setManualOnSearchStrategyOnTextCampaign(String login, long campaignId) {
        campaignsUpdateWithCheck(
                login,
                new UpdateRequestMap().withCampaigns(
                        new CampaignUpdateItemMap()
                                .withId(campaignId)
                                .withTextCampaign(
                                        new TextCampaignUpdateItemMap().withBiddingStrategy(
                                                new TextCampaignStrategyMap()
                                                        .withSearch(new TextCampaignSearchStrategyMap()
                                                                .defaultHighestPosition())
                                                        .withNetwork(new TextCampaignNetworkStrategyMap()
                                                                .defaultServingOff())
                                        ))));
    }

    public void setMaximumCoverageStrategyOnTextCampaign(long campaignId) {
        setMaximumCoverageStrategyOnTextCampaign(null, campaignId);
    }

    public void setMaximumCoverageStrategyOnTextCampaign(String login, long campaignId) {
        campaignsUpdateWithCheck(
                login,
                new UpdateRequestMap().withCampaigns(
                        new CampaignUpdateItemMap()
                                .withId(campaignId)
                                .withTextCampaign(
                                        new TextCampaignUpdateItemMap().withBiddingStrategy(
                                                new TextCampaignStrategyMap()
                                                        .withSearch(new TextCampaignSearchStrategyMap()
                                                                .defaultHighestPosition())
                                                        .withNetwork(new TextCampaignNetworkStrategyMap()
                                                                .defaultMaximumCoverage())
                                        ))));
    }

    public void setShowsDisabledOnContextStrategyOnTextCampaign(long campaignId) {
        setShowsDisabledOnContextStrategyOnTextCampaign(null, campaignId);
    }

    public void setShowsDisabledOnContextStrategyOnTextCampaign(String login, long campaignId) {
        campaignsUpdateWithCheck(
                login,
                new UpdateRequestMap().withCampaigns(
                        new CampaignUpdateItemMap()
                                .withId(campaignId)
                                .withTextCampaign(
                                        new TextCampaignUpdateItemMap().withBiddingStrategy(
                                                new TextCampaignStrategyMap()
                                                        .withSearch(new TextCampaignSearchStrategyMap()
                                                                .defaultHighestPosition())
                                                        .withNetwork(new TextCampaignNetworkStrategyMap()
                                                                .defaultServingOff())
                                        ))));
    }

    @Step("[Campaigns]: Suspend")
    public List<Long> campaignsSuspend(String login, Long... campaignIds) {
        SuspendRequestMap request =
                new SuspendRequestMap().withSelectionCriteria(new IdsCriteriaMap().withIds(campaignIds));
        List<ActionResult> results = ((SuspendResponse) defaultClientV5()
                .invokeMethod(ServiceNames.CAMPAIGNS_EXT, login, Action.SUSPEND, request.getBean())).getSuspendResults();
        lookForResponseExceptions(results);
        return getIDsFromActionResults(results);
    }

    public List<Long> campaignsSuspend(Long... campaignIds) {
        return campaignsSuspend(null, campaignIds);
    }


    public void expectErrorOnCampaignsSuspend(SuspendRequestMap parameters, String login, Api5Error api5Error) {
        shouldGetErrorOn(ServiceNames.CAMPAIGNS_EXT, login, Action.SUSPEND, parameters, api5Error);
    }

    public void expectErrorOnCampaignsSuspend(SuspendRequestMap parameters, Api5Error api5Error) {
        expectErrorOnCampaignsSuspend(parameters, null, api5Error);
    }

    public void expectErrorOnCampaignsSuspend(Object parameters, String login, Api5Error api5Error) {
        shouldGetJSONErrorOn(ServiceNames.CAMPAIGNS_EXT, login, Action.SUSPEND, parameters, api5Error);
    }

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

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

    public List<Long> campaignsResume(Long... campaignIds) {
        return campaignsResume(null, campaignIds);
    }

    public void expectErrorOnCampaignsResume(ResumeRequestMap parameters, String login, Api5Error api5Error) {
        shouldGetErrorOn(ServiceNames.CAMPAIGNS_EXT, login, Action.RESUME, parameters, api5Error);
    }

    public void expectErrorOnCampaignsResume(ResumeRequestMap parameters, Api5Error api5Error) {
        expectErrorOnCampaignsResume(parameters, null, api5Error);
    }

    public void expectErrorOnCampaignsResume(Object parameters, String login, Api5Error api5Error) {
        shouldGetJSONErrorOn(ServiceNames.CAMPAIGNS_EXT, login, Action.RESUME, parameters, api5Error);
    }

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

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

    public List<Long> campaignsDelete(Long... campaignIds) {
        return campaignsDelete(null, campaignIds);
    }

    public void expectErrorOnCampaignsDelete(DeleteRequestMap parameters, String login, Api5Error api5Error) {
        shouldGetErrorOn(ServiceNames.CAMPAIGNS_EXT, login, Action.DELETE, parameters, api5Error);
    }

    public void expectErrorOnCampaignsDelete(DeleteRequestMap parameters, Api5Error api5Error) {
        expectErrorOnCampaignsDelete(parameters, null, api5Error);
    }

    public void expectErrorOnCampaignsDelete(Object parameters, String login, Api5Error api5Error) {
        shouldGetJSONErrorOn(ServiceNames.CAMPAIGNS_EXT, login, Action.DELETE, parameters, api5Error);
    }

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

    @Step("[Campaigns]: Archive")
    public List<Long> campaignsArchive(String login, Long... campaignIds) {
        ArchiveRequestMap request =
                new ArchiveRequestMap().withSelectionCriteria(new IdsCriteriaMap().withIds(campaignIds));
        List<ActionResult> results = ((ArchiveResponse) defaultClientV5()
                .invokeMethod(ServiceNames.CAMPAIGNS_EXT, login, Action.ARCHIVE, request.getBean())).getArchiveResults();
        lookForResponseExceptions(results);
        return getIDsFromActionResults(results);
    }

    public List<Long> campaignsArchive(Long... campaignIds) {
        return campaignsArchive(null, campaignIds);
    }

    public void expectErrorOnCampaignsArchive(ArchiveRequestMap parameters, String login, Api5Error api5Error) {
        shouldGetErrorOn(ServiceNames.CAMPAIGNS_EXT, login, Action.ARCHIVE, parameters, api5Error);
    }

    public void expectErrorOnCampaignsArchive(ArchiveRequestMap parameters, Api5Error api5Error) {
        expectErrorOnCampaignsArchive(parameters, null, api5Error);
    }

    public void expectErrorOnCampaignsArchive(Object parameters, String login, Api5Error api5Error) {
        shouldGetJSONErrorOn(ServiceNames.CAMPAIGNS_EXT, login, Action.ARCHIVE, parameters, api5Error);
    }

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

    @Step("[Campaigns]: Unarchive")
    public List<Long> campaignsUnarchive(String login, Long... campaignIds) {
        UnarchiveRequestMap request =
                new UnarchiveRequestMap().withSelectionCriteria(new IdsCriteriaMap().withIds(campaignIds));
        List<ActionResult> results = ((UnarchiveResponse) defaultClientV5()
                .invokeMethod(ServiceNames.CAMPAIGNS_EXT, login, Action.UNARCHIVE, request.getBean()))
                .getUnarchiveResults();
        lookForResponseExceptions(results);
        return getIDsFromActionResults(results);
    }

    public List<Long> campaignsUnarchive(Long... campaignIds) {
        return campaignsUnarchive(null, campaignIds);
    }

    public void expectErrorOnCampaignsUnarchive(UnarchiveRequestMap parameters, String login, Api5Error api5Error) {
        shouldGetErrorOn(ServiceNames.CAMPAIGNS_EXT, login, Action.UNARCHIVE, parameters, api5Error);
    }

    public void expectErrorOnCampaignsUnarchive(UnarchiveRequestMap parameters, Api5Error api5Error) {
        expectErrorOnCampaignsUnarchive(parameters, null, api5Error);
    }

    public void expectErrorOnCampaignsUnarchive(Object parameters, String login, Api5Error api5Error) {
        shouldGetJSONErrorOn(ServiceNames.CAMPAIGNS_EXT, login, Action.UNARCHIVE, parameters, api5Error);
    }

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

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

    public <T extends BeanMap> Object shouldGetResultOn(String login, Action action, T args,
                                                        ExpectedResult... expectedResults)
    {
        return shouldGetResultOn(login, action, args == null ? null : args.getBean(), expectedResults);
    }

    @Step("Проверяем ID/Warnings/Errors в ответе сервиса Campaigns")
    public Object shouldGetResultOn(String login, Action action, Object args,
                                    ExpectedResult... expectedResults)
    {
        Object response = defaultClientV5()
                .invokeMethod(ServiceNames.CAMPAIGNS_EXT, login, action, args);
        ArrayList<ActionResult> actualResult = extractActionResults(response, action);
        if (action.equals(Action.DELETE)) {
            List<Long> ids = getIDsFromActionResults(actualResult);
            removeCampaignsFromQueue(ids);
        }
        if (action.equals(Action.ADD)) {
            List<Long> ids = getIDsFromActionResults(actualResult);
            putCampaignToQueue(login, ids);
        }
        checkActionResults(actualResult, expectedResults);
        return response;
    }

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

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

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

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

    private void lookForResponseExceptions(List<ActionResult> results) {
        List<ActionResult> errors = select(results, having(on(ActionResult.class).getErrors(), not(emptyIterable())));

        assertThat("отсутствуют ошибки при вызове метода", errors, emptyIterable());
    }

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

    public List<Long> addDefaultTextCampaigns(int amount) {
        return addDefaultTextCampaigns(null, amount);
    }
    //endregion

    private List<Long> extractIdsFromAddResponse(AddResponse addResponse) {
        return filter(notNullValue(), extract(addResponse.getAddResults(), on(ActionResult.class).getId()));
    }

    public Callable<Boolean> stateChanged(Long campaignId, CampaignStateGetEnum state) {
        return () -> getCampaigns(campaignId).get(0).getState() == state;
    }

    public Callable<Boolean> campaignFundsChangedOnFunds(CampaignGetItem campaignBefore, Money sum) {
        return campaignFundsChangedOnFunds(campaignBefore, sum.bidLong().longValue());
    }

    public Callable<Boolean> campaignFundsChangedOnFunds(CampaignGetItem campaignBefore, Long sum) {
        return campaignFundsChangedOnFunds(null, campaignBefore, sum, properties.isDirectUseRealNotifyOrder());
    }

    public Callable<Boolean> campaignFundsChangedOnFunds(
            CampaignGetItem campaignBefore, Long sum, boolean billingNotifications)
    {
        return campaignFundsChangedOnFunds(null, campaignBefore, sum, billingNotifications);
    }

    public Callable<Boolean> campaignFundsChangedOnFunds(String login, CampaignGetItem campaignBefore, Money sum) {
        return campaignFundsChangedOnFunds(login, campaignBefore, sum.bidLong().longValue(),
                properties.isDirectUseRealNotifyOrder());
    }

    public Callable<Boolean> campaignFundsChangedOnFunds(String login, CampaignGetItem campaignBefore, Long sum) {
        return campaignFundsChangedOnFunds(login, campaignBefore, sum, properties.isDirectUseRealNotifyOrder());
    }

    public Callable<Boolean> campaignFundsChangedOnFunds(String login,
                                                         final CampaignGetItem campaignBefore, final Long sum, final boolean billingNotifications)
    {
        if (campaignBefore.getFunds().getMode() == CampaignFundsEnum.SHARED_ACCOUNT_FUNDS) {
            throw new AssertionError("Кампания подключена к ОС");
        }
        return () -> {
            Long sumBefore = campaignBefore.getFunds().getCampaignFunds().getSum();
            CampaignGetItem campaignAfter = campaignsGet(login, campaignBefore.getId(),
                    CampaignFieldEnum.FUNDS, CampaignFieldEnum.ID, CampaignFieldEnum.CURRENCY);
            if (!billingNotifications) {
                UserSteps.getInstance().balanceSteps().synchronizeWithBalance(campaignAfter.getId());
            }
            Long sumAfter = campaignAfter.getFunds().getCampaignFunds().getSum();
            log.info(String.format("Баланс кампании: был %s, ожидается: %s",
                    Money.valueOf(sumAfter), Money.valueOf(sumBefore).add(sum)));
            Currency currency = Currency.getFor(campaignAfter.getCurrency().value());
            return Money.valueOf(sumAfter - sumBefore, currency).subtractVAT().longValue().equals(sum);
        };
    }

    /**
     * Метод используется для новых кампаний, у которых не было денег
     *
     * @param campaignId
     * @return
     */
    public Callable<Boolean> campaignSumChanged(String login, final Long campaignId) {
        return campaignSumChanged(login, campaignId, properties.isDirectUseRealNotifyOrder());
    }

    public Callable<Boolean> campaignSumChanged(String login, final Long campaignId,
                                                final boolean billingNotifications)
    {
        CampaignGetItem campaignForCheckWallet = getCampaigns(login, campaignId).get(0);
        if (campaignForCheckWallet.getFunds().getMode().equals(CampaignFundsEnum.SHARED_ACCOUNT_FUNDS)) {
            throw new AssertionError("Кампания подключена к ОС");
        }
        return () -> {
            if (!billingNotifications) {
                UserSteps.getInstance().balanceSteps().synchronizeWithBalance(campaignId);
            }
            log.info("Wait sum > 0");
            CampaignGetItem campaign = getCampaigns(login, campaignId).get(0);
            return !Long.valueOf(0).equals(campaign.getFunds().getCampaignFunds().getSum());
        };
    }

    public Callable<Long> campaignSumIs(String login, final Long campaignId) {
        return campaignSumIs(login, campaignId, properties.isDirectUseRealNotifyOrder());
    }

    public Callable<Long> campaignSumIs(
            String login, final Long campaignId, final boolean billingNotifications)
    {
        CampaignGetItem campaignForCheckWallet = getCampaigns(login, campaignId).get(0);
        if (campaignForCheckWallet.getFunds().getMode().equals(CampaignFundsEnum.SHARED_ACCOUNT_FUNDS)) {
            throw new AssertionError("Кампания подключена к ОС");
        }
        return () -> {
            if (!billingNotifications) {
                UserSteps.getInstance().balanceSteps().synchronizeWithBalance(campaignId);
            }
            CampaignGetItem campaign = getCampaigns(login, campaignId).get(0);
            return campaign.getFunds().getCampaignFunds().getSum();
        };
    }

    public Callable<Long> campaignRestIs(String login, final Long campaignId) {
        return campaignRestIs(login, campaignId, properties.isDirectUseRealNotifyOrder());
    }

    public Callable<Long> campaignRestIs(
            String login, final Long campaignId, final boolean billingNotifications)
    {
        CampaignGetItem campaignForCheckWallet = getCampaigns(login, campaignId).get(0);
        if (campaignForCheckWallet.getFunds().getMode().equals(CampaignFundsEnum.SHARED_ACCOUNT_FUNDS)) {
            throw new AssertionError("Кампания подключена к ОС");
        }
        return () -> {
            if (!billingNotifications) {
                UserSteps.getInstance().balanceSteps().synchronizeWithBalance(campaignId);
            }
            CampaignGetItem campaign = getCampaigns(login, campaignId).get(0);
            return campaign.getFunds().getCampaignFunds().getBalance();
        };
    }
}
