package ru.yandex.autotests.direct.api.ads.unarchive;

import java.sql.Timestamp;
import java.time.Duration;
import java.time.Instant;
import java.util.List;

import com.yandex.direct.api.v5.ads.AdFieldEnum;
import com.yandex.direct.api.v5.ads.GetResponse;
import com.yandex.direct.api.v5.general.StateEnum;
import org.junit.Before;
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.BannerImagesStatusmoderate;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.BannersMinusGeoType;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.BannersPhoneflag;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.BannersStatusarch;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.BannersStatusbssynced;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.BannersStatusmoderate;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.BannersStatuspostmoderate;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.BannersStatussitelinksmoderate;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.CampaignsStatusmoderate;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.PhrasesStatusmoderate;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.PhrasesStatuspostmoderate;
import ru.yandex.autotests.direct.db.models.jooq.ppc.tables.records.BannersRecord;
import ru.yandex.autotests.direct.db.models.jooq.ppc.tables.records.BidsRecord;
import ru.yandex.autotests.direct.db.models.jooq.ppc.tables.records.CampaignsRecord;
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.tags.TagDictionary;
import ru.yandex.autotests.directapi.apiclient.config.Semaphore;
import ru.yandex.autotests.directapi.enums.ImageType;
import ru.yandex.autotests.directapi.model.api5.ads.AdAddItemMap;
import ru.yandex.autotests.directapi.model.api5.ads.AdsSelectionCriteriaMap;
import ru.yandex.autotests.directapi.model.api5.ads.GetRequestMap;
import ru.yandex.autotests.directapi.model.api5.ads.TextAdAddMap;
import ru.yandex.autotests.directapi.rules.ApiSteps;
import ru.yandex.autotests.directapi.rules.Trashman;
import ru.yandex.qatools.Tag;
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.allure.annotations.Issues;
import ru.yandex.qatools.allure.annotations.Step;
import ru.yandex.qatools.hazelcast.SemaphoreRule;

import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static ru.yandex.autotests.irt.testutils.allure.TestSteps.assertThat;
import static ru.yandex.autotests.irt.testutils.allure.TestSteps.assumeThat;

/**
 * Created by semkagtn on 22.04.15.
 * https://st.yandex-team.ru/TESTIRT-5211
 */
@Aqua.Test
@Features(AdsFeatures.UNARCHIVE)
@Description("Проверка влияния Unarchive на статусы объявления и его объектов в случае непустых групп объявлений")
@Issues({
        @Issue("https://st.yandex-team.ru/DIRECT-34573"),
        @Issue("https://st.yandex-team.ru/DIRECT-66288")
})
@Tag(TagDictionary.TRUNK)
public class CheckStatusesAfterUnarchiveAdFromNotEmptyAdGroupTest {

    protected static final String LOGIN = AdsLogins.CLIENT15;

    @ClassRule
    public static ApiSteps api = new ApiSteps().version(104).as(LOGIN);

    @ClassRule
    public static SemaphoreRule semaphore = Semaphore.getSemaphore();
    protected static Long campaignID;
    protected static String adImageHash;
    private static Long vCardID;
    private static Long sitelinksSetID;
    @Rule
    public Trashman trasher = new Trashman(api);
    protected Long adGroupID;
    protected Long adID;

    @BeforeClass
    public static void createAdGroup() {
        api.userSteps.clientFakeSteps().fakeClearClientSpentUnits(LOGIN);

        campaignID = api.userSteps.campaignSteps().addDefaultTextCampaign(LOGIN);
        vCardID = api.userSteps.vCardsSteps().addDefaultVCard(campaignID);
        sitelinksSetID = api.userSteps.sitelinksSteps().addDefaultSet(1);
        adImageHash = api.userSteps.imagesSteps().configLoginImages(LOGIN, ImageType.REGULAR, 1)[0];
    }

    private static Timestamp oneHourAgo() {
        return Timestamp.from(Instant.now().minus(Duration.ofHours(1)));
    }

    @Before
    @Step("Подготовка данных для теста")
    public void createAd() {
        api.userSteps.clientFakeSteps().fakeClearClientSpentUnits(LOGIN);

        // создаём не пустую группу
        adGroupID = api.userSteps.adGroupsSteps().addDefaultGroup(campaignID);
        api.userSteps.keywordsSteps().addDefaultKeyword(adGroupID);

        adID = api.userSteps.adsSteps().addAd(new AdAddItemMap()
                .withAdGroupId(adGroupID)
                .withTextAd(new TextAdAddMap()
                        .defaultTextAd()
                        .withAdImageHash(adImageHash)
                        .withVCardId(vCardID)
                        .withSitelinkSetId(sitelinksSetID)));
        api.userSteps.bannersFakeSteps().makeBannersModerated(adID);
        api.userSteps.adsSteps().adsArchive(adID);

        GetResponse response = api.userSteps.adsSteps().adsGet(new GetRequestMap()
                .withSelectionCriteria(new AdsSelectionCriteriaMap().withIds(adID))
                .withFieldNames(AdFieldEnum.ID, AdFieldEnum.STATE));
        StateEnum state = response.getAds().get(0).getState();

        assumeThat("объявление в архиве", state, equalTo(StateEnum.ARCHIVED));
    }

    @Test
    @Description("Изменение значения поля StatusArch после разархивации объявления")
    public void statusArchAfterUnarchiveAd() {
        BannersStatusarch statusBefore = jooqSteps(LOGIN).bannersSteps().getBanner(adID).getStatusarch();
        assumeThat("значение StatusArch = Yes", statusBefore, equalTo(BannersStatusarch.Yes));
        api.userSteps.adsSteps().adsUnarchive(adID);
        BannersStatusarch statusAfter = jooqSteps(LOGIN).bannersSteps().getBanner(adID).getStatusarch();
        assertThat("значение StatusArch изменилось на No", statusAfter, equalTo(BannersStatusarch.No));
    }

    @Test
    @Description("Изменение значения поля StatusBsSynced после разархивации объявления")
    public void statusBsSyncedAfterUnarchiveAd() {
        jooqSteps(LOGIN).bannersSteps().updateBanners(
                new BannersRecord().setBid(adID).setStatusbssynced(BannersStatusbssynced.Yes));
        BannersStatusbssynced statusBefore = jooqSteps(LOGIN)
                .bannersSteps().getBanner(adID).getStatusbssynced();
        assumeThat("значение StatusBsSynced = Yes", statusBefore, equalTo(BannersStatusbssynced.Yes));
        api.userSteps.adsSteps().adsUnarchive(adID);
        BannersStatusbssynced statusAfter = jooqSteps(LOGIN)
                .bannersSteps().getBanner(adID).getStatusbssynced();
        assertThat("значение StatusBsSynced изменилось на No",
                statusAfter, equalTo(BannersStatusbssynced.No));
    }

    @Test
    @Description("Изменение значения поля LastChange после разархивации объявления")
    public void lastChangeAfterUnarchiveAd() {
        api.userSteps.bannersFakeSteps().setLastChange(adID, oneHourAgo().toString());
        Timestamp lastChangeBefore = jooqSteps(LOGIN).bannersSteps().getBanner(adID).getLastchange();

        api.userSteps.adsSteps().adsUnarchive(adID);
        Timestamp lastChangeAfter = jooqSteps(LOGIN).bannersSteps().getBanner(adID).getLastchange();

        assertThat("значение LastChange изменилось", lastChangeAfter, not(equalTo(lastChangeBefore)));
    }

    @Test
    @Description("Изменение значения поля phrases.statusModerate после разархивации объявления," +
            " когда не все объявления группы заархивированы")
    public void phrasesStatusModerateAfterUnarchiveAdWhenNotAllBannersAreArchived() {
        api.userSteps.adsSteps().addDefaultTextAd(adGroupID);
        PhrasesStatusmoderate statusBefore = jooqSteps(LOGIN)
                .adGroupsSteps().getPhrases(adGroupID).getStatusmoderate();
        api.userSteps.adsSteps().adsUnarchive(adID);
        PhrasesStatusmoderate statusAfter = jooqSteps(LOGIN)
                .adGroupsSteps().getPhrases(adGroupID).getStatusmoderate();
        assertThat("значение phrases.statusModerate не изменилось",
                statusAfter, equalTo(statusBefore));
    }

    @Test
    @Description("Изменение значения поля phrases.statusPostModerate после разархивации объявления," +
            " когда statusPostModerate == Rejected")
    public void phrasesStatusPostModerateAfterUnarchiveAdRejected() {
        jooqSteps(LOGIN).adGroupsSteps().updatePhrases(
                new PhrasesRecord().setPid(adGroupID).setStatuspostmoderate(PhrasesStatuspostmoderate.Rejected));
        PhrasesStatuspostmoderate statusBefore = jooqSteps(LOGIN)
                .adGroupsSteps().getPhrases(adGroupID).getStatuspostmoderate();
        assumeThat("значение phrases.statusPostModerate = Rejected",
                statusBefore, equalTo(PhrasesStatuspostmoderate.Rejected));
        api.userSteps.adsSteps().adsUnarchive(adID);
        PhrasesStatuspostmoderate statusAfter = jooqSteps(LOGIN)
                .adGroupsSteps().getPhrases(adGroupID).getStatuspostmoderate();
        assertThat("значение phrases.statusPostModerate = Rejected",
                statusAfter, equalTo(PhrasesStatuspostmoderate.Rejected));
    }

    @Test
    @Description("Изменение значения поля phrases.statusPostModerate после разархивации объявления," +
            " когда statusPostModerate != Rejected")
    public void phrasesStatusPostModerateAfterUnarchiveAdNotRejected() {
        PhrasesStatuspostmoderate statusBefore = jooqSteps(LOGIN)
                .adGroupsSteps().getPhrases(adGroupID).getStatuspostmoderate();
        assumeThat("значение phrases.statusPostModerate != Rejected",
                statusBefore, not(equalTo(PhrasesStatuspostmoderate.Rejected)));
        api.userSteps.adsSteps().adsUnarchive(adID);
        PhrasesStatuspostmoderate statusAfter = jooqSteps(LOGIN)
                .adGroupsSteps().getPhrases(adGroupID).getStatuspostmoderate();
        assertThat("значение phrases.statusPostModerate изменилось на No",
                statusAfter, equalTo(PhrasesStatuspostmoderate.No));
    }

    @Test
    @Description("Изменение значения поля campaigns.statusModerate после разархивации объявления," +
            " когда campaigns.statusModerate != (New, No)")
    public void campaignStatusPostModerateAfterUnarchiveAdYes() {
        jooqSteps(LOGIN).campaignsSteps().updateCampaigns(
                new CampaignsRecord().setCid(campaignID).setStatusmoderate(CampaignsStatusmoderate.Yes));
        CampaignsStatusmoderate statusBefore = jooqSteps(LOGIN)
                .campaignsSteps().getCampaignById(campaignID).getStatusmoderate();
        assumeThat("значение campaigns.statusModerate == Yes", statusBefore, equalTo(CampaignsStatusmoderate.Yes));
        api.userSteps.adsSteps().adsUnarchive(adID);
        CampaignsStatusmoderate statusAfter = jooqSteps(LOGIN)
                .campaignsSteps().getCampaignById(campaignID).getStatusmoderate();
        assertThat("значение campaigns.statusModerate не изменилось", statusAfter,
                equalTo(CampaignsStatusmoderate.Yes));
    }

    @Test
    @Description("Изменение значения поля StatusModerate после разархивации объявления")
    public void shouldChangeStatusModerateAfterUnarchiveAd() {
        BannersStatusmoderate statusBefore = jooqSteps(LOGIN).bannersSteps().getBanner(adID).getStatusmoderate();
        assumeThat("значение StatusModerate != Ready",
                statusBefore, not(equalTo(BannersStatusmoderate.Ready)));
        api.userSteps.adsSteps().adsUnarchive(adID);
        BannersStatusmoderate statusAfter = jooqSteps(LOGIN).bannersSteps().getBanner(adID).getStatusmoderate();
        assertThat("значение StatusModerate изменилось на Ready",
                statusAfter, equalTo(BannersStatusmoderate.Ready));
    }

    @Test
    @Description("Изменение значения поля StatusPostModerate после разархивации объявления")
    public void shouldChangeStatusPostModerateAfterUnarchiveAd() {
        jooqSteps(LOGIN).bannersSteps().setBannerStatusPostModerate(adID, BannersStatuspostmoderate.Yes);
        BannersStatuspostmoderate statusBefore = jooqSteps(LOGIN)
                .bannersSteps().getBanner(adID).getStatuspostmoderate();
        assumeThat("значение StatusPostModerate = Yes",
                statusBefore, equalTo(BannersStatuspostmoderate.Yes));
        api.userSteps.adsSteps().adsUnarchive(adID);
        BannersStatuspostmoderate statusAfter = jooqSteps(LOGIN)
                .bannersSteps().getBanner(adID).getStatuspostmoderate();
        assertThat("значение StatusPostModerate изменилось на No",
                statusAfter, equalTo(BannersStatuspostmoderate.No));
    }

    @Test
    @Description("Изменение значения поля phoneflag после разархивации объявления")
    public void shouldChangePhoneFlagAfterUnarchiveAd() {
        BannersPhoneflag flagBefore = jooqSteps(LOGIN).bannersSteps().getBanner(adID).getPhoneflag();
        assumeThat("значение phoneflag != Ready", flagBefore, not(equalTo(BannersPhoneflag.Ready)));
        api.userSteps.adsSteps().adsUnarchive(adID);
        BannersPhoneflag flagAfter = jooqSteps(LOGIN).bannersSteps().getBanner(adID).getPhoneflag();
        assertThat("значение phoneflag изменилось на Ready", flagAfter, equalTo(BannersPhoneflag.Ready));
    }

    @Test
    @Description("Изменение значения поля sitelinks_status_moderate после разархивации объявления")
    public void shouldChangeSitelinksStatusModerateAfterUnarchiveAd() {
        BannersStatussitelinksmoderate statusBefore = jooqSteps(LOGIN)
                .bannersSteps().getBanner(adID).getStatussitelinksmoderate();
        assumeThat("значение sitelinks_status_moderate != Ready",
                statusBefore, not(equalTo(BannersStatussitelinksmoderate.Ready)));
        api.userSteps.adsSteps().adsUnarchive(adID);
        BannersStatussitelinksmoderate statusAfter = jooqSteps(LOGIN)
                .bannersSteps().getBanner(adID).getStatussitelinksmoderate();
        assertThat("значение sitelinks_status_moderate изменилось на Ready",
                statusAfter, equalTo(BannersStatussitelinksmoderate.Ready));
    }

    @Test
    @Description("Изменение значения поля banner_images.statusModerate после разархивации объявления")
    public void shouldChangeImageStatusModerateAfterUnarchiveAd() {
        BannerImagesStatusmoderate statusBefore = jooqSteps(LOGIN)
                .imagesSteps().getBannerImagesByBid(adID).getStatusmoderate();
        assumeThat("значение banner_images.statusModerate != Ready",
                statusBefore, not(equalTo(BannerImagesStatusmoderate.Ready)));
        api.userSteps.adsSteps().adsUnarchive(adID);
        BannerImagesStatusmoderate statusAfter = jooqSteps(LOGIN)
                .imagesSteps().getBannerImagesByBid(adID).getStatusmoderate();
        assertThat("значение banner_images.statusModerate изменилось на Ready",
                statusAfter, equalTo(BannerImagesStatusmoderate.Ready));
    }

    @Test
    @Description("Изменение значения поля phrases.statusModerate после разархивации объявления" +
            ", когда все объявления группы заархивированы")
    public void shouldChangePhrasesStatusModerateAfterUnarchiveAdWhenAllBannersAreArchived() {
        PhrasesStatusmoderate statusBefore = jooqSteps(LOGIN)
                .adGroupsSteps().getPhrases(adGroupID).getStatusmoderate();
        assumeThat("значение phrases.statusModerate != Ready",
                statusBefore, not(equalTo(PhrasesStatusmoderate.Ready)));
        api.userSteps.adsSteps().adsUnarchive(adID);
        PhrasesStatusmoderate statusAfter = jooqSteps(LOGIN)
                .adGroupsSteps().getPhrases(adGroupID).getStatusmoderate();
        assertThat("значение phrases.statusModerate изменилось на Ready",
                statusAfter, equalTo(PhrasesStatusmoderate.Ready));
    }

    @Test
    @Description("Изменение значения поля campaigns.statusModerate после разархивации объявления," +
            " когда campaigns.statusModerate = New")
    public void shouldChangeCampaignStatusPostModerateAfterUnarchiveAdNew() {
        jooqSteps(LOGIN).campaignsSteps().updateCampaigns(
                new CampaignsRecord().setCid(campaignID).setStatusmoderate(CampaignsStatusmoderate.New));
        CampaignsStatusmoderate statusBefore = jooqSteps(LOGIN)
                .campaignsSteps().getCampaignById(campaignID).getStatusmoderate();
        assumeThat("значение campaigns.statusModerate == New", statusBefore, equalTo(CampaignsStatusmoderate.New));
        api.userSteps.adsSteps().adsUnarchive(adID);
        CampaignsStatusmoderate statusAfter = jooqSteps(LOGIN)
                .campaignsSteps().getCampaignById(campaignID).getStatusmoderate();
        assertThat("значение campaigns.statusModerate изменилось на Ready",
                statusAfter, equalTo(CampaignsStatusmoderate.Ready));
    }

    @Test
    @Description("Изменение значения поля campaigns.statusModerate после разархивации объявления," +
            " когда campaigns.statusModerate = No")
    public void shouldChangeCampaignStatusPostModerateAfterUnarchiveAdNo() {
        jooqSteps(LOGIN).campaignsSteps().updateCampaigns(
                new CampaignsRecord().setCid(campaignID).setStatusmoderate(CampaignsStatusmoderate.No));
        CampaignsStatusmoderate statusBefore = jooqSteps(LOGIN)
                .campaignsSteps().getCampaignById(campaignID).getStatusmoderate();
        assumeThat("значение campaigns.statusModerate == No", statusBefore, equalTo(CampaignsStatusmoderate.No));
        api.userSteps.adsSteps().adsUnarchive(adID);
        CampaignsStatusmoderate statusAfter = jooqSteps(LOGIN)
                .campaignsSteps().getCampaignById(campaignID).getStatusmoderate();
        assertThat("значение campaigns.statusModerate изменилось на Ready",
                statusAfter, equalTo(CampaignsStatusmoderate.Ready));
    }

    @Test
    public void shouldDeleteMinusGeo() {
        String bannerMinusGeo = "1,2,3";
        jooqSteps(LOGIN).bannersSteps().saveBannersMinusGeo(adID, BannersMinusGeoType.current, bannerMinusGeo);
        assertThat("начальное значение минус-гео корректное",
                jooqSteps(LOGIN).bannersSteps().getBannersMinusGeo(adID).get(0).getMinusGeo(), equalTo(bannerMinusGeo));
        api.userSteps.adsSteps().adsUnarchive(adID);
        assertThat("минус-гео баннера удалены", jooqSteps(LOGIN).bannersSteps().getBannersMinusGeo(adID),
                empty());
    }

    protected DirectJooqDbSteps jooqSteps(String login) {
        return api.userSteps.getDirectJooqDbSteps().useShardForLogin(login);
    }

}
