package ru.yandex.autotests.direct.api.campaigns.update.textcampaign.strategy.averagecrr;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

import com.google.gson.JsonObject;
import com.yandex.direct.api.v5.campaigns.TextCampaignSearchStrategyTypeEnum;
import com.yandex.direct.api.v5.general.AttributionModelEnum;
import org.junit.Before;
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.campaigns.CampaignsFeatures;
import ru.yandex.autotests.direct.api.campaigns.CampaignsLogins;
import ru.yandex.autotests.direct.db.models.jooq.ppc.tables.records.CampaignsRecord;
import ru.yandex.autotests.direct.db.steps.DirectJooqDbSteps;
import ru.yandex.autotests.direct.utils.money.Money;
import ru.yandex.autotests.direct.utils.tags.TagDictionary;
import ru.yandex.autotests.directapi.apiclient.config.Semaphore;
import ru.yandex.autotests.directapi.model.api5.campaigns.CampaignUpdateItemMap;
import ru.yandex.autotests.directapi.model.api5.campaigns.StrategyAverageCrrMap;
import ru.yandex.autotests.directapi.model.api5.campaigns.TextCampaignNetworkStrategyMap;
import ru.yandex.autotests.directapi.model.api5.campaigns.TextCampaignSearchStrategyMap;
import ru.yandex.autotests.directapi.model.api5.campaigns.TextCampaignStrategyMap;
import ru.yandex.autotests.directapi.model.api5.campaigns.TextCampaignUpdateItemMap;
import ru.yandex.autotests.directapi.model.api5.campaigns.UpdateRequestMap;
import ru.yandex.autotests.directapi.model.api5.general.ExpectedResult;
import ru.yandex.autotests.directapi.model.campaigns.MetrikaGoals;
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.Step;
import ru.yandex.qatools.hazelcast.SemaphoreRule;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.core.IsNull.notNullValue;
import static ru.yandex.autotests.irt.testutils.allure.TestSteps.assertThat;

@Aqua.Test
@Features(CampaignsFeatures.UPDATE)
@Description("Проверка изменения времени последнего рестарта ДРР стратегии")
@Tag(TagDictionary.TRUNK)
public class ConversionStrategyLastBidderRestartTimeTest {
    public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final String client = CampaignsLogins.CLIENT_STRATEGY_WITH_LAST_BIDDER_RESTART_TIME;
    private static final List<Long> goalIds = MetrikaGoals.getGoalsForLogin(client,2);
    private static final Money weekSpendLimit = Money.valueOf(10_000_000);
    private static final Long RESTART_TIME_DELTA = 10L;
    @ClassRule
    public static ApiSteps api = new ApiSteps().as(client);
    @ClassRule
    public static SemaphoreRule semaphore = Semaphore.getSemaphore();
    @Rule
    public Trashman trasher = new Trashman(api);
    private DirectJooqDbSteps dbSteps = api.userSteps.getDirectJooqDbSteps().useShardForLogin(client);

    private Long campaignId;

    @Before
    @Step("Подготовим данные для теста")
    public void prepare() {
        Long clientId = dbSteps.usersSteps().getUser(client).getClientid();
        dbSteps.featuresSteps().setClientFeature(clientId, "conversion_strategy_learning_status_enabled", true);
        api.userSteps.clientFakeSteps().fakeClearClientSpentUnits(client);

        dbSteps = api.userSteps.getDirectJooqDbSteps().useShardForLogin(client);
    }

    @Test
    public void changeStrategyToConversion_LastRestartTimeReset() {
        campaignId = addAvgCrrCampaign();
        api.userSteps.campaignSteps().shouldGetResultOnUpdate(
                updateRequest(new StrategyAverageCrrMap()
                        .defaultStrategyAverageCrr()
                        .withWeeklySpendLimit(weekSpendLimit.bidLong().longValue())
                        .withGoalId(goalIds.get(1))),
                ExpectedResult.success());
        LocalDateTime lastRestartTime = getRestartTime();

        assertThat("Время рестарта есть", lastRestartTime, notNullValue());
    }


    @Test
    public void changeStrategyGoal_LastRestartTimeReset() {
        campaignId = addAvgCrrCampaign();
        pushInPastRestartTime(campaignId, RESTART_TIME_DELTA);
        LocalDateTime oldLastRestartTime = getRestartTime();

        api.userSteps.campaignSteps().shouldGetResultOnUpdate(
                updateRequest(new StrategyAverageCrrMap()
                        .defaultStrategyAverageCrr()
                        .withWeeklySpendLimit(weekSpendLimit.bidLong().longValue())
                        .withGoalId(goalIds.get(1))),
                ExpectedResult.success());

        LocalDateTime newRestartTime = getRestartTime();
        assertThat("Время рестарта есть", newRestartTime, notNullValue());
        assertThat("Время рестарта изменилось после обновления", newRestartTime, not(equalTo(oldLastRestartTime)));
    }

    @Test
    public void changeAttributionModel_LastRestartTimeReset() {
        campaignId = addAvgCrrCampaign();
        pushInPastRestartTime(campaignId, RESTART_TIME_DELTA);
        LocalDateTime oldLastRestartTime = getRestartTime();

        api.userSteps.campaignSteps().shouldGetResultOnUpdate(
                new UpdateRequestMap().withCampaigns(new CampaignUpdateItemMap()
                        .withId(campaignId)
                        .withTextCampaign(new TextCampaignUpdateItemMap()
                                .withAttributionModel(AttributionModelEnum.FC)
                                .withBiddingStrategy(new TextCampaignStrategyMap()
                                        .withSearch(new TextCampaignSearchStrategyMap()
                                                .withBiddingStrategyType(TextCampaignSearchStrategyTypeEnum.AVERAGE_CRR)
                                                .withAverageCrr(new StrategyAverageCrrMap()
                                                        .defaultStrategyAverageCrr()
                                                        .withWeeklySpendLimit(weekSpendLimit.bidLong().longValue())
                                                        .withGoalId(goalIds.get(0))))
                                        .withNetwork(new TextCampaignNetworkStrategyMap().defaultServingOff())))),
                ExpectedResult.success());

        LocalDateTime newRestartTime = getRestartTime();

        assertThat("Время рестарта есть", newRestartTime, notNullValue());
        assertThat("Время рестарта изменилось после обновления", newRestartTime, not(equalTo(oldLastRestartTime)));

    }

    @Test
    public void changeBudget_LastRestartTimeNotReset() {
        campaignId = addAvgCrrCampaign();
        pushInPastRestartTime(campaignId, RESTART_TIME_DELTA);
        LocalDateTime oldLastRestartTime = getRestartTime();

        api.userSteps.campaignSteps().shouldGetResultOnUpdate(
                updateRequest(new StrategyAverageCrrMap()
                        .defaultStrategyAverageCrr()
                        .withWeeklySpendLimit(weekSpendLimit.bidLong().longValue() + 1)
                        .withGoalId(goalIds.get(0))),
                ExpectedResult.success());

        LocalDateTime newRestartTime = getRestartTime();

        assertThat("Время рестарта есть", newRestartTime, notNullValue());
        assertThat("Время рестарта изменилось после обновления", newRestartTime, equalTo(oldLastRestartTime));

    }

    private Long addAvgCrrCampaign() {
        campaignId = api.userSteps.campaignSteps().addDefaultTextCampaign();
        addGoals();
        api.userSteps.campaignSteps().shouldGetResultOnUpdate(
                updateRequest(new StrategyAverageCrrMap()
                        .defaultStrategyAverageCrr()
                        .withWeeklySpendLimit(weekSpendLimit.bidLong().longValue())
                        .withGoalId(goalIds.get(0))), ExpectedResult.success());
        return campaignId;
    }

    private void addGoals() {
        dbSteps.campMetrikaGoalsSteps()
                .addOrUpdateMetrikaGoals(campaignId, goalIds.get(0), 100L, 100L);
        dbSteps.campMetrikaGoalsSteps()
                .addOrUpdateMetrikaGoals(campaignId, goalIds.get(1), 100L, 100L);
    }

    private LocalDateTime getRestartTime() {
        CampaignsRecord campaignsRecord = dbSteps.campaignsSteps().getCampaignById(campaignId);
        JsonObject strategyData = (JsonObject) campaignsRecord.getStrategyData();

        return LocalDateTime.parse(strategyData.get("last_bidder_restart_time").getAsString(), DATE_TIME_FORMATTER);
    }

    // Отодвигаем в прошлое RestartTime на deltaSeconds секунд, чтобы гарантировать различие во времени
    private void pushInPastRestartTime(Long campaignId, Long deltaSeconds) {
        CampaignsRecord campaignsRecord = dbSteps.campaignsSteps().getCampaignById(campaignId);
        JsonObject strategyData = (JsonObject) campaignsRecord.getStrategyData();
        LocalDateTime oldLastRestartTime =
                LocalDateTime.parse(strategyData.get("last_bidder_restart_time").getAsString(), DATE_TIME_FORMATTER);
        api.userSteps.getDirectJooqDbSteps().campaignsSteps().setStrategyDataLastBidderRestartTimeIfExists(campaignId,
                oldLastRestartTime.minusSeconds(deltaSeconds).format(DATE_TIME_FORMATTER));
    }

    private UpdateRequestMap updateRequest(StrategyAverageCrrMap strategyAverageCrrMap) {
        return new UpdateRequestMap().withCampaigns(new CampaignUpdateItemMap()
                .withId(campaignId)
                .withTextCampaign(new TextCampaignUpdateItemMap()
                        .withBiddingStrategy(new TextCampaignStrategyMap()
                                .withSearch(new TextCampaignSearchStrategyMap()
                                        .withBiddingStrategyType(TextCampaignSearchStrategyTypeEnum.AVERAGE_CRR)
                                        .withAverageCrr(strategyAverageCrrMap))
                                .withNetwork(new TextCampaignNetworkStrategyMap().defaultServingOff()))));
    }
}
