package ru.yandex.autotests.directintapi.bstransport.main.group.minus_geo;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import ru.yandex.aqua.annotations.project.Aqua;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.BannersMinusGeoType;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.BannersStatusbssynced;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.BannersStatuspostmoderate;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.PhrasesStatusbssynced;
import ru.yandex.autotests.direct.db.models.jooq.ppc.tables.records.BannersMinusGeoRecord;
import ru.yandex.autotests.direct.db.models.jooq.ppc.tables.records.BannersRecord;
import ru.yandex.autotests.direct.db.models.jooq.ppc.tables.records.PhrasesRecord;
import ru.yandex.autotests.direct.db.steps.DirectJooqDbSteps;
import ru.yandex.autotests.direct.utils.model.RegionIDValues;
import ru.yandex.autotests.direct.utils.tags.TagDictionary;
import ru.yandex.autotests.directapi.darkside.Logins;
import ru.yandex.autotests.directapi.darkside.connection.Semaphore;
import ru.yandex.autotests.directapi.darkside.model.RunBsTransportScriptResponse;
import ru.yandex.autotests.directapi.darkside.model.bslogs.StopFlag;
import ru.yandex.autotests.directapi.darkside.model.bslogs.UpdateInfo;
import ru.yandex.autotests.directapi.darkside.model.bslogs.clientdata.Banner;
import ru.yandex.autotests.directapi.darkside.model.bslogs.clientdata.Campaign;
import ru.yandex.autotests.directapi.darkside.model.bslogs.clientdata.Context;
import ru.yandex.autotests.directapi.rules.ApiSteps;
import ru.yandex.autotests.directintapi.bstransport.FeatureNames;
import ru.yandex.autotests.directintapi.bstransport.StoriesNames;
import ru.yandex.qatools.Tag;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Issue;
import ru.yandex.qatools.allure.annotations.Step;
import ru.yandex.qatools.allure.annotations.Stories;
import ru.yandex.qatools.allure.annotations.Title;
import ru.yandex.qatools.hazelcast.SemaphoreRule;

import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
import static ru.yandex.autotests.directintapi.bstransport.TransportTestUtils.regionListToString;
import static ru.yandex.autotests.irt.testutils.allure.TestSteps.assumeThat;

/**
 * Created by n-boy on 03.03.17.
 * https://st.yandex-team.ru/TESTIRT-11106
 */

@Aqua.Test
@Tag(TagDictionary.RELEASE)
@Issue("https://st.yandex-team.ru/DIRECT-63267")
@Title("Транспорт: применение минус-регионов, для двух, побывавших в БК баннеров")
@Stories(StoriesNames.MINUS_GEO)
@Features(FeatureNames.GROUPS)
@RunWith(Parameterized.class)
public class BsTransportOfMinusGeoForTextTwoOldBannersTest {
    private static final String login = Logins.LOGIN_TRANSPORT;
    private static int shard;
    private static Long cid;
    private static Long pid;
    private static Long bid1;
    private static Long bid2;
    private static Context context;
    private static Banner banner1;
    private static Banner banner2;

    private static String defaultAdgroupSrcGeo = regionListToString(
            Arrays.asList(RegionIDValues.BELARUS, RegionIDValues.UKRAINE, RegionIDValues.RUSSIA, RegionIDValues.TURKEY));
    private static String defaultBanner1CurentMinusGeo = regionListToString(Arrays.asList(RegionIDValues.BELARUS));
    private static String defaultBanner2CurentMinusGeo = regionListToString(Arrays.asList(RegionIDValues.UKRAINE));
    private static String defaultBanner1BsSyncedMinusGeo = regionListToString(Arrays.asList(RegionIDValues.RUSSIA));
    private static String defaultBanner2BsSyncedMinusGeo = regionListToString(Arrays.asList(RegionIDValues.TURKEY));
    private static String adgroupGeoExceptCurrent1Current2 = regionListToString(Arrays.asList(RegionIDValues.RUSSIA, RegionIDValues.TURKEY));
    private static String adgroupGeoExceptCurrent1BsSynced2 = regionListToString(Arrays.asList(RegionIDValues.UKRAINE, RegionIDValues.RUSSIA));
    private static String adgroupGeoExceptCurrent1 = regionListToString(Arrays.asList(RegionIDValues.UKRAINE, RegionIDValues.RUSSIA, RegionIDValues.TURKEY));

    private static DirectJooqDbSteps dbSteps;

    private static HashMap<Integer, Boolean> testDataPreparedWithParams = new HashMap<Integer, Boolean>();

    @ClassRule
    public static ApiSteps api = new ApiSteps().as(login);

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

    @Parameterized.Parameter(0)
    public int testCaseNumber;

    @Parameterized.Parameter(1)
    public BannersStatuspostmoderate banner2StatusPostModerate;

    @Parameterized.Parameter(2)
    public String resContextGeo;

    @Parameterized.Parameter(3)
    public UpdateInfo resBanner2UpdateInfo;

    @Parameterized.Parameter(4)
    public StopFlag resBanner2Stop;

    @Parameterized.Parameter(5)
    public String resBanner2BsSyncedMinusGeo;

    @Parameterized.Parameters(name = "Номер тест-кейса: {0}, свойства группы/баннеров: "
            + "banner2StatusPostModerate={1}" )
    public static Collection<Object[]> testData() {
        return Arrays.asList(
                new Object[]{39, BannersStatuspostmoderate.Yes, adgroupGeoExceptCurrent1Current2, UpdateInfo.UPDATE, StopFlag.RESUME, defaultBanner2CurentMinusGeo},
                new Object[]{40, BannersStatuspostmoderate.No, adgroupGeoExceptCurrent1BsSynced2, UpdateInfo.SKIP, StopFlag.RESUME, defaultBanner2BsSyncedMinusGeo},
                new Object[]{41, BannersStatuspostmoderate.Rejected, adgroupGeoExceptCurrent1, UpdateInfo.SKIP, StopFlag.STOP, defaultBanner2BsSyncedMinusGeo}
        );
    }

    @BeforeClass
    @Step("Создаем новую кампанию, с группой и двумя баннерами, полностью модерируем и отправляем в БК")
    public static void beforeClass() {
        shard = api.userSteps.clientFakeSteps().getUserShard(login);
        dbSteps =  api.userSteps.getDirectJooqDbSteps().useShard(shard);

        cid = api.userSteps.campaignSteps().addDefaultTextCampaign();

        api.userSteps.campaignFakeSteps().makeNewCampaignReadyForSendingToBS(cid);

        pid = api.userSteps.adGroupsSteps().addDefaultGroup(cid);
        Long keywordId = api.userSteps.keywordsSteps().addDefaultKeyword(pid);

        api.userSteps.groupFakeSteps().makeGroupFullyModerated(pid);
        api.userSteps.phrasesFakeSteps().makeKeywordModerated(keywordId);

        bid1 = api.userSteps.adsSteps().addDefaultTextAd(pid);
        bid2 = api.userSteps.adsSteps().addDefaultTextAd(pid);

        api.userSteps.bannersFakeSteps().makeBannerFullyModerated(bid1);
        api.userSteps.bannersFakeSteps().makeBannerFullyModerated(bid2);


        RunBsTransportScriptResponse
                resp = api.userSteps.getDarkSideSteps().getTransportSteps().sendNewCampaign(shard, cid);
        Campaign campaign = api.userSteps.getDarkSideSteps().getTransportSteps().getClientDataRequestCampaign(resp, 0, cid);
        assumeThat("новая кампания отправлена в БК", campaign, notNullValue());
        context = campaign != null ? campaign.getContext(pid) : null;
        banner1 = context != null ? context.getBanner(bid1) : null;
        assumeThat("в БК отправлен баннер 1, первой итерацией", banner1, notNullValue());
        banner2 = context != null ? context.getBanner(bid2) : null;
        assumeThat("в БК отправлен баннер 2, первой итерацией", banner2, notNullValue());
    }

    @Before
    @Step("Применяем к созданным ранее группе/баннеру свойства из параметров теста, и запускаем итерацию транспорта")
    public void prepareTestData() {
        if (testDataPreparedWithParams.getOrDefault(testCaseNumber, false)) {
            return;
        }

        PhrasesRecord phrasesRecord = dbSteps.adGroupsSteps().getPhrases(pid);
        phrasesRecord.setStatusbssynced(PhrasesStatusbssynced.No);
        phrasesRecord.setGeo(defaultAdgroupSrcGeo);
        dbSteps.adGroupsSteps().updatePhrases(phrasesRecord);

        BannersRecord banner1Record = dbSteps.bannersSteps().getBanner(bid1);
        banner1Record.setStatusbssynced(BannersStatusbssynced.No);
        dbSteps.bannersSteps().updateBanners(banner1Record);

        BannersRecord banner2Record = dbSteps.bannersSteps().getBanner(bid2);
        banner2Record.setStatusbssynced(BannersStatusbssynced.No);
        banner2Record.setStatuspostmoderate(banner2StatusPostModerate);
        dbSteps.bannersSteps().updateBanners(banner2Record);

        dbSteps.bannersSteps().saveBannersMinusGeo(bid1, BannersMinusGeoType.current, defaultBanner1CurentMinusGeo);
        dbSteps.bannersSteps().saveBannersMinusGeo(bid1, BannersMinusGeoType.bs_synced, defaultBanner1BsSyncedMinusGeo);

        dbSteps.bannersSteps().saveBannersMinusGeo(bid2, BannersMinusGeoType.current, defaultBanner2CurentMinusGeo);
        dbSteps.bannersSteps().saveBannersMinusGeo(bid2, BannersMinusGeoType.bs_synced, defaultBanner2BsSyncedMinusGeo);

        api.userSteps.campaignFakeSteps().setStatusBsSynced(cid, "No");
        RunBsTransportScriptResponse resp = api.userSteps.getDarkSideSteps().getTransportSteps().sendSyncedCampaign(shard, cid);
        Campaign campaign = api.userSteps.getDarkSideSteps().getTransportSteps().getClientDataRequestCampaign(resp, cid);
        context = campaign != null ? campaign.getContext(pid) : null;
        banner1 = context != null ? context.getBanner(bid1) : null;
        banner2 = context != null ? context.getBanner(bid2) : null;

        testDataPreparedWithParams.put(testCaseNumber, true);
    }

    @Test
    @Title("Отправка в БК группы баннеров с учетом минус-регионов, проверяем Context.UpdateInfo")
    public void testContextUpdateInfo() {
        assertThat("условия показа отправлены в БК", context, notNullValue());
        assertThat("условия показа отправлены в БК с UpdateInfo=1",
                context.getUpdateInfo(), equalTo(1));
    }

    @Test
    @Title("Отправка в БК баннера 1 с учетом минус-регионов, проверяем Banner.UpdateInfo")
    public void testBanner1UpdateInfo() {
        checkBannerUpdateInfo(banner1, UpdateInfo.UPDATE);
    }

    @Test
    @Title("Отправка в БК баннера 2 с учетом минус-регионов, проверяем Banner.UpdateInfo")
    public void testBanner2UpdateInfo() {
        checkBannerUpdateInfo(banner2, resBanner2UpdateInfo);
    }

    @Test
    @Title("Отправка в БК баннера 1 с учетом минус-регионов, проверяем Banner.Stop")
    public void testBanner1Stop() {
        checkBannerStop(banner1, StopFlag.RESUME);
    }

    @Test
    @Title("Отправка в БК баннера 2 с учетом минус-регионов, проверяем Banner.Stop")
    public void testBanner2Stop() {
        checkBannerStop(banner2, resBanner2Stop);
    }

    @Test
    @Title("Отправка в БК группы баннеров с учетом минус-регионов, проверяем Context.Geo")
    public void testContextGeo() {
        if (resContextGeo != null) {
            assumeThat("условия показа отправлены в БК", context, notNullValue());
            String[] resContextGeoList;
            if (resContextGeo.equals("")) {
                resContextGeoList = new String[]{};
            } else {
                resContextGeoList = resContextGeo.split(",");
            }
            assertThat("условия показа отправлены в БК с соответствующим значением Geo",
                    context.getGeo(), containsInAnyOrder(resContextGeoList));
        }
    }

    @Test
    @Title("Отправка в БК баннера 1 с учетом минус-регионов, проверяем состояние записи в БД banners_minus_geo[type=bs_synced]")
    public void testBanner1BsSyncedMinusGeo() {
        checkBannerBsSyncedMinusGeo(bid1, defaultBanner1CurentMinusGeo);
    }

    @Test
    @Title("Отправка в БК баннера 2 с учетом минус-регионов, проверяем состояние записи в БД banners_minus_geo[type=bs_synced]")
    public void testBanner2BsSyncedMinusGeo() {
        checkBannerBsSyncedMinusGeo(bid2, resBanner2BsSyncedMinusGeo);
    }

    @Step("Проверяем Banner.UpdateInfo")
    public void checkBannerUpdateInfo(Banner banner, UpdateInfo resBannerUpdateInfo) {
        if (resBannerUpdateInfo == null) {
            assertThat("баннер не отправлен в БК", banner, nullValue());
        } else {
            assumeThat("баннер отправлен в БК", banner, notNullValue());
            assertThat("баннер отправлен в БК с соответствующим значением UpdateInfo",
                    banner.getUpdateInfo(), equalTo(resBannerUpdateInfo.value()));
        }
    }

    @Step("Проверяем Banner.Stop")
    public void checkBannerStop(Banner banner, StopFlag resBannerStop) {
        if (resBannerStop != null) {
            assumeThat("баннер отправлен в БК", banner, notNullValue());
            assertThat("баннер отправлен в БК с соответствующим значением Stop",
                    banner.getStop(), equalTo(resBannerStop.value()));
        }
    }

    @Step("Проверяем состояние записи в БД banners_minus_geo[type=bs_synced]")
    public void checkBannerBsSyncedMinusGeo(Long bid, String resBannerBsSyncedMinusGeo) {
        BannersMinusGeoRecord minusGeo = dbSteps.bannersSteps().getBannersMinusGeo(bid, BannersMinusGeoType.bs_synced);
        if (resBannerBsSyncedMinusGeo == null) {
            assertThat("баннер после отправки в БК, в БД находится с соответствующими минус-регионами типа bs_synced",
                    minusGeo, nullValue());
        } else {
            assertThat("баннер после отправки в БК, в БД находится с соответствующими минус-регионами типа bs_synced",
                    minusGeo == null ? null : minusGeo.getMinusGeo(), equalTo(resBannerBsSyncedMinusGeo));
        }
    }

}
