package ru.yandex.autotests.direct.api.ads.update.textad.calloutsetting;

import java.util.Arrays;
import java.util.List;

import com.yandex.direct.api.v5.ads.AddResponse;
import com.yandex.direct.api.v5.general.OperationEnum;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;

import ru.yandex.aqua.annotations.project.Aqua;
import ru.yandex.autotests.direct.api.ads.AdsFeatures;
import ru.yandex.autotests.direct.api.ads.AdsLogins;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.BannersAdditionsAdditionsType;
import ru.yandex.autotests.direct.db.models.jooq.ppc.tables.records.BannersAdditionsRecord;
import ru.yandex.autotests.direct.db.steps.DirectJooqDbSteps;
import ru.yandex.autotests.directapi.apiclient.config.Semaphore;
import ru.yandex.autotests.directapi.apiclient.errors.Api5ErrorDetails;
import ru.yandex.autotests.directapi.darkside.datacontainers.jsonrpc.fake.BannerFakeInfo;
import ru.yandex.autotests.directapi.darkside.model.Status;
import ru.yandex.autotests.directapi.model.api5.adextensions.AdExtensionAddItemMap;
import ru.yandex.autotests.directapi.model.api5.adextensiontypes.AdExtensionSettingItemMap;
import ru.yandex.autotests.directapi.model.api5.adextensiontypes.AdExtensionSettingMap;
import ru.yandex.autotests.directapi.model.api5.adextensiontypes.CalloutMap;
import ru.yandex.autotests.directapi.model.api5.ads.AdAddItemMap;
import ru.yandex.autotests.directapi.model.api5.ads.AdUpdateItemMap;
import ru.yandex.autotests.directapi.model.api5.ads.AddRequestMap;
import ru.yandex.autotests.directapi.model.api5.ads.TextAdAddMap;
import ru.yandex.autotests.directapi.model.api5.ads.TextAdUpdateMap;
import ru.yandex.autotests.directapi.model.api5.ads.UpdateRequestMap;
import ru.yandex.autotests.directapi.model.api5.general.ExpectedResult;
import ru.yandex.autotests.directapi.model.api5.general.Notification;
import ru.yandex.autotests.directapi.rules.ApiSteps;
import ru.yandex.autotests.directapi.rules.Trashman;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Issue;
import ru.yandex.qatools.hazelcast.SemaphoreRule;

import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
import static org.apache.commons.lang3.StringUtils.capitalize;
import static org.hamcrest.CoreMatchers.equalTo;
import static ru.yandex.autotests.direct.db.utils.JooqRecordListDifferMatcher.recordsDiffer;
import static ru.yandex.autotests.directapi.model.api5.general.JavaOrPerlExpectedResult.Common.fieldMustBePositiveInteger;
import static ru.yandex.autotests.irt.testutils.allure.TestSteps.assertThat;
import static ru.yandex.autotests.irt.testutils.allure.TestSteps.assumeThat;

/**
 * Created by pavryabov on 16.03.16.
 * https://st.yandex-team.ru/TESTIRT-8642
 */
@Aqua.Test
@Features(AdsFeatures.UPDATE)
@Description("Проверка обновления уточнений операцией SET")
@Issue("https://st.yandex-team.ru/DIRECT-50726")
public class UpdateCalloutsOperationSetTest {

    @ClassRule
    public static ApiSteps api = new ApiSteps().version(104).as(AdsLogins.CLIENT);
    private static DirectJooqDbSteps jooqDbSteps;

    @ClassRule
    public static SemaphoreRule semaphore = Semaphore.getSemaphore();

    @Rule
    public Trashman trasher = new Trashman(api);

    private static Long adExtensionId;
    private static Long adExtensionIdElse;
    private static Long adGroupId;
    private static Long campaignId;


    @BeforeClass
    public static void initTest() {
        api.userSteps.clientFakeSteps().fakeClearClientSpentUnits(AdsLogins.CLIENT);
        jooqDbSteps = api.userSteps.getDirectJooqDbSteps().useShardForLogin(AdsLogins.CLIENT);
        campaignId = api.userSteps.campaignSteps().addDefaultTextCampaign();
        adGroupId = api.userSteps.adGroupsSteps().addDefaultGroup(campaignId);
        api.userSteps.keywordsSteps().addDefaultKeyword(adGroupId);
        adExtensionId = api.userSteps.adExtensionsSteps().addCalloutWithText(randomAlphabetic(15));
        adExtensionIdElse = api.userSteps.adExtensionsSteps().addCalloutWithText(randomAlphabetic(15));
    }

    @Test
    public void setOneCalloutToAdWithoutCallouts() {
        Long adId = api.userSteps.adsSteps().addDefaultTextAd(adGroupId);
        api.userSteps.adsSteps().shouldGetResultOnUpdate(new UpdateRequestMap()
                        .withAds(new AdUpdateItemMap()
                                .withId(adId)
                                .withTextAd(new TextAdUpdateMap()
                                        .defaultAd()
                                        .withCalloutSetting(new AdExtensionSettingMap()
                                                .withAdExtensions(new AdExtensionSettingItemMap()
                                                        .withAdExtensionId(adExtensionId)
                                                        .withOperation(OperationEnum.SET))))),
                ExpectedResult.success(adId));

        List<BannersAdditionsRecord> bannersAdditions =
                jooqDbSteps.bannerAdditionsSteps().getBannersAdditionsRecordsByBid(adId);
        BannersAdditionsRecord expectedBannersAdditions =
                new BannersAdditionsRecord(adId, adExtensionId, BannersAdditionsAdditionsType.callout, (short) 1);

        assertThat("дополнения правильно привязались к объявлению",
                bannersAdditions, recordsDiffer(Arrays.asList(expectedBannersAdditions)));
    }

    @Test
    public void setOneCalloutToAdWithCallout() {
        Long adId = api.userSteps.adsSteps().addAd(new AdAddItemMap()
                .withAdGroupId(adGroupId)
                .withTextAd(new TextAdAddMap()
                        .defaultTextAd()
                        .withAdExtensionIds(adExtensionId)));
        api.userSteps.adsSteps().shouldGetResultOnUpdate(new UpdateRequestMap()
                        .withAds(new AdUpdateItemMap()
                                .withId(adId)
                                .withTextAd(new TextAdUpdateMap()
                                        .defaultAd()
                                        .withCalloutSetting(new AdExtensionSettingMap()
                                                .withAdExtensions(new AdExtensionSettingItemMap()
                                                        .withAdExtensionId(adExtensionIdElse)
                                                        .withOperation(OperationEnum.SET))))),
                ExpectedResult.success(adId));

        List<BannersAdditionsRecord> bannersAdditions =
                jooqDbSteps.bannerAdditionsSteps().getBannersAdditionsRecordsByBid(adId);
        BannersAdditionsRecord expectedBannersAdditionsElse =
                new BannersAdditionsRecord(adId, adExtensionIdElse, BannersAdditionsAdditionsType.callout, (short) 1);

        assertThat("дополнения правильно привязались к объявлению",
                bannersAdditions, recordsDiffer(Arrays.asList(expectedBannersAdditionsElse)));
    }

    @Test
    public void setAlreadyLinkedCallout() {
        Long adId = api.userSteps.adsSteps().addAd(new AdAddItemMap()
                .withAdGroupId(adGroupId)
                .withTextAd(new TextAdAddMap()
                        .defaultTextAd()
                        .withAdExtensionIds(adExtensionId)));
        api.userSteps.adsSteps().shouldGetResultOnUpdate(new UpdateRequestMap()
                        .withAds(new AdUpdateItemMap()
                                .withId(adId)
                                .withTextAd(new TextAdUpdateMap()
                                        .defaultAd()
                                        .withCalloutSetting(new AdExtensionSettingMap()
                                                .withAdExtensions(new AdExtensionSettingItemMap()
                                                        .withAdExtensionId(adExtensionId)
                                                        .withOperation(OperationEnum.SET))))),
                ExpectedResult.success(adId));

        List<BannersAdditionsRecord> bannersAdditions =
                jooqDbSteps.bannerAdditionsSteps().getBannersAdditionsRecordsByBid(adId);
        BannersAdditionsRecord expectedBannersAdditions =
                new BannersAdditionsRecord(adId, adExtensionId, BannersAdditionsAdditionsType.callout, (short) 1);

        assertThat("дополнения правильно привязались к объявлению",
                bannersAdditions, recordsDiffer(Arrays.asList(expectedBannersAdditions)));
    }

    @Test
    public void setTwoCalloutsToAdWithoutCallouts() {
        Long adId = api.userSteps.adsSteps().addDefaultTextAd(adGroupId);
        api.userSteps.adsSteps().shouldGetResultOnUpdate(new UpdateRequestMap()
                        .withAds(new AdUpdateItemMap()
                                .withId(adId)
                                .withTextAd(new TextAdUpdateMap()
                                        .defaultAd()
                                        .withCalloutSetting(new AdExtensionSettingMap()
                                                .withAdExtensions(new AdExtensionSettingItemMap()
                                                                .withAdExtensionId(adExtensionId)
                                                                .withOperation(OperationEnum.SET),
                                                        new AdExtensionSettingItemMap()
                                                                .withAdExtensionId(adExtensionIdElse)
                                                                .withOperation(OperationEnum.SET))))),
                ExpectedResult.success(adId));

        List<BannersAdditionsRecord> bannersAdditions =
                jooqDbSteps.bannerAdditionsSteps().getBannersAdditionsRecordsByBid(adId);
        BannersAdditionsRecord expectedBannersAdditions =
                new BannersAdditionsRecord(adId, adExtensionId, BannersAdditionsAdditionsType.callout, (short) 1);
        BannersAdditionsRecord expectedBannersAdditionsElse =
                new BannersAdditionsRecord(adId, adExtensionIdElse, BannersAdditionsAdditionsType.callout, (short) 2);

        assertThat("дополнения правильно привязались к объявлению",
                bannersAdditions, recordsDiffer(Arrays.asList(expectedBannersAdditions, expectedBannersAdditionsElse)));
    }

    @Test
    public void setOneValidAndOneInvalidCallouts() {
        Long adId = api.userSteps.adsSteps().addDefaultTextAd(adGroupId);
        api.userSteps.adsSteps().shouldGetResultOnUpdate(new UpdateRequestMap()
                        .withAds(new AdUpdateItemMap()
                                .withId(adId)
                                .withTextAd(new TextAdUpdateMap()
                                        .defaultAd()
                                        .withCalloutSetting(new AdExtensionSettingMap()
                                                .withAdExtensions(new AdExtensionSettingItemMap()
                                                                .withAdExtensionId(adExtensionId)
                                                                .withOperation(OperationEnum.SET),
                                                        new AdExtensionSettingItemMap()
                                                                .withAdExtensionId(0l)
                                                                .withOperation(OperationEnum.SET))))),
                fieldMustBePositiveInteger(
                        capitalize(TextAdUpdateMap.CALLOUT_SETTING) + "." +
                                capitalize(AdExtensionSettingMap.AD_EXTENSIONS) + "[2]." +
                                capitalize(AdExtensionSettingItemMap.AD_EXTENSION_ID)));
    }

    @Test
    public void setSeveralSameCallouts() {
        Long adId = api.userSteps.adsSteps().addDefaultTextAd(adGroupId);
        api.userSteps.adsSteps().shouldGetResultOnUpdate(new UpdateRequestMap()
                        .withAds(new AdUpdateItemMap()
                                .withId(adId)
                                .withTextAd(new TextAdUpdateMap()
                                        .defaultAd()
                                        .withCalloutSetting(new AdExtensionSettingMap()
                                                .withAdExtensions(new AdExtensionSettingItemMap()
                                                                .withAdExtensionId(adExtensionId)
                                                                .withOperation(OperationEnum.SET),
                                                        new AdExtensionSettingItemMap()
                                                                .withAdExtensionId(adExtensionId)
                                                                .withOperation(OperationEnum.SET))))),
                ExpectedResult.errors(new Notification(9802, Api5ErrorDetails.VALUE_CANNOT_BE_PRESENT_IN_FIELD_MORE_THAN_ONCE,
                        adExtensionId,
                        capitalize(TextAdUpdateMap.CALLOUT_SETTING) + "." +
                                capitalize(AdExtensionSettingMap.AD_EXTENSIONS) + "." +
                                capitalize(AdExtensionSettingItemMap.AD_EXTENSION_ID))));
    }

    @Test
    public void setMaxAmountOfCalloutsInAdToAdWithCallout() {
        Long adId = api.userSteps.adsSteps().addAd(new AdAddItemMap()
                .withAdGroupId(adGroupId)
                .withTextAd(new TextAdAddMap()
                        .defaultTextAd()
                        .withAdExtensionIds(adExtensionId)));

        AdExtensionAddItemMap[] adExtensionAddItemMaps =
                new AdExtensionAddItemMap[TextAdAddMap.MAX_AD_EXTENSION_IDS_IN_AD];
        for (int i = 0; i < TextAdAddMap.MAX_AD_EXTENSION_IDS_IN_AD; i++) {
            adExtensionAddItemMaps[i] = new AdExtensionAddItemMap()
                    .withCallout(new CalloutMap()
                            .withCalloutText(randomAlphabetic(15)));
        }
        List<Long> adExtensionIds = api.userSteps.adExtensionsSteps().addAdExtensions(adExtensionAddItemMaps);
        AdExtensionSettingItemMap[] adExtensionSettingItemMaps =
                new AdExtensionSettingItemMap[TextAdAddMap.MAX_AD_EXTENSION_IDS_IN_AD];
        for (int i = 0; i < TextAdAddMap.MAX_AD_EXTENSION_IDS_IN_AD; i++) {
            adExtensionSettingItemMaps[i] = new AdExtensionSettingItemMap()
                    .withAdExtensionId(adExtensionIds.get(i))
                    .withOperation(OperationEnum.SET);
        }
        api.userSteps.adsSteps().shouldGetResultOnUpdate(new UpdateRequestMap()
                        .withAds(new AdUpdateItemMap()
                                .withId(adId)
                                .withTextAd(new TextAdUpdateMap()
                                        .withCalloutSetting(new AdExtensionSettingMap()
                                                .withAdExtensions(adExtensionSettingItemMaps)))),
                ExpectedResult.success(adId));
    }

    @Test
    public void setMoreThanMaxAmountOfCalloutsInAd() {
        AdExtensionAddItemMap[] adExtensionAddItemMaps =
                new AdExtensionAddItemMap[TextAdAddMap.MAX_AD_EXTENSION_IDS_IN_AD + 1];
        for (int i = 0; i < TextAdAddMap.MAX_AD_EXTENSION_IDS_IN_AD + 1; i++) {
            adExtensionAddItemMaps[i] = new AdExtensionAddItemMap()
                    .withCallout(new CalloutMap()
                            .withCalloutText(randomAlphabetic(15)));
        }
        List<Long> adExtensionIds = api.userSteps.adExtensionsSteps().addAdExtensions(adExtensionAddItemMaps);
        Long adId = api.userSteps.adsSteps().addDefaultTextAd(adGroupId);

        AdExtensionSettingItemMap[] adExtensionSettingItemMaps =
                new AdExtensionSettingItemMap[TextAdAddMap.MAX_AD_EXTENSION_IDS_IN_AD + 1];
        for (int i = 0; i < TextAdAddMap.MAX_AD_EXTENSION_IDS_IN_AD + 1; i++) {
            adExtensionSettingItemMaps[i] = new AdExtensionSettingItemMap()
                    .withAdExtensionId(adExtensionIds.get(i))
                    .withOperation(OperationEnum.SET);
        }
        api.userSteps.adsSteps().shouldGetResultOnUpdate(new UpdateRequestMap()
                        .withAds(new AdUpdateItemMap()
                                .withId(adId)
                                .withTextAd(new TextAdUpdateMap()
                                        .withCalloutSetting(new AdExtensionSettingMap()
                                                .withAdExtensions(adExtensionSettingItemMaps)))),
                ExpectedResult.errors(
                        new Notification(7000,
                                Api5ErrorDetails.LIMIT_OF_AD_EXTENSIONS_FOR_AD_EXCEEDED,
                                TextAdAddMap.MAX_AD_EXTENSION_IDS_IN_AD)));
    }

    @Test
    public void setOneCalloutToAdAndCheckStatusBsSynced() {
        //DIRECT-52237
        //DIRECT-52715
        Long adId = api.userSteps.adsSteps().addAd(new AdAddItemMap()
                .withAdGroupId(adGroupId)
                .withTextAd(new TextAdAddMap()
                        .defaultTextAd()
                        .withAdExtensionIds(adExtensionId)));
        api.userSteps.bannersFakeSteps().setStatusBsSynced(adId, Status.YES);
        BannerFakeInfo bannerFakeInfo = api.userSteps.bannersFakeSteps().getBannerParams(adId);
        assumeThat("объявление синхронизировано с БК", bannerFakeInfo.getStatusBsSynced(), equalTo(Status.YES));
        api.userSteps.adsSteps().shouldGetResultOnUpdate(new UpdateRequestMap()
                        .withAds(new AdUpdateItemMap()
                                .withId(adId)
                                .withTextAd(new TextAdUpdateMap()
                                        .defaultAd()
                                        .withCalloutSetting(new AdExtensionSettingMap()
                                                .withAdExtensions(new AdExtensionSettingItemMap()
                                                        .withAdExtensionId(adExtensionIdElse)
                                                        .withOperation(OperationEnum.SET))))),
                ExpectedResult.success(adId));
        bannerFakeInfo = api.userSteps.bannersFakeSteps().getBannerParams(adId);
        assumeThat("StatusBsSynced объявления сбросился", bannerFakeInfo.getStatusBsSynced(), equalTo(Status.NO));
    }

    @Test
    @Description("Set уже привязанный набор уточнений, но в другом порядке")
    @Issue("https://st.yandex-team.ru/DIRECT-53172")
    public void setTwoCalloutsWithOtherSequences() {
        AddResponse response = api.userSteps.adsSteps().shouldGetResultOnAdd(new AddRequestMap()
                        .withAds(new AdAddItemMap()
                                .withTextAd(new TextAdAddMap()
                                        .defaultTextAd()
                                        .withAdExtensionIds(adExtensionId, adExtensionIdElse))
                                .withAdGroupId(adGroupId)),
                ExpectedResult.success());
        Long adId = response.getAddResults().get(0).getId();

        List<BannersAdditionsRecord> bannersAdditions =
                jooqDbSteps.bannerAdditionsSteps().getBannersAdditionsRecordsByBid(adId);
        BannersAdditionsRecord expectedBannersAdditions =
                new BannersAdditionsRecord(adId, adExtensionId, BannersAdditionsAdditionsType.callout, (short) 1);
        BannersAdditionsRecord expectedBannersAdditionsElse =
                new BannersAdditionsRecord(adId, adExtensionIdElse, BannersAdditionsAdditionsType.callout, (short) 2);

        assertThat("дополнения правильно привязались к объявлению",
                bannersAdditions, recordsDiffer(Arrays.asList(expectedBannersAdditions, expectedBannersAdditionsElse)));

        api.userSteps.adsSteps().shouldGetResultOnUpdate(new UpdateRequestMap()
                        .withAds(new AdUpdateItemMap()
                                .withId(adId)
                                .withTextAd(new TextAdUpdateMap()
                                        .withCalloutSetting(new AdExtensionSettingMap()
                                                .withAdExtensions(new AdExtensionSettingItemMap()
                                                                .withAdExtensionId(adExtensionIdElse)
                                                                .withOperation(OperationEnum.SET),
                                                        new AdExtensionSettingItemMap()
                                                                .withAdExtensionId(adExtensionId)
                                                                .withOperation(OperationEnum.SET))))),
                ExpectedResult.success(adId));

        bannersAdditions =
                jooqDbSteps.bannerAdditionsSteps().getBannersAdditionsRecordsByBid(adId);
        expectedBannersAdditions =
                new BannersAdditionsRecord(adId, adExtensionId, BannersAdditionsAdditionsType.callout, (short) 2);
        expectedBannersAdditionsElse =
                new BannersAdditionsRecord(adId, adExtensionIdElse, BannersAdditionsAdditionsType.callout, (short) 1);

        assertThat("дополнения правильно привязались к объявлению",
                bannersAdditions, recordsDiffer(Arrays.asList(expectedBannersAdditions, expectedBannersAdditionsElse)));
    }
}
