package ru.yandex.autotests.directintapi.bstransport.main.cpmbanner.transmit;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;

import org.junit.After;
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.cmd.rules.CreativeBannerRule;
import ru.yandex.autotests.direct.cmd.rules.DirectCmdRule;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.BannersBannerType;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.BannersPerformanceStatusmoderate;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.PerfCreativesStatusmoderate;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.PhrasesAdgroupType;
import ru.yandex.autotests.direct.db.models.jooq.ppc.tables.records.AdgroupAdditionalTargetingsRecord;
import ru.yandex.autotests.direct.db.steps.DirectJooqDbSteps;
import ru.yandex.autotests.direct.utils.campaigns.CampaignTypeEnum;
import ru.yandex.autotests.direct.utils.tags.TagDictionary;
import ru.yandex.autotests.direct.web.api.core.DirectRule;
import ru.yandex.autotests.direct.web.api.models.Condition;
import ru.yandex.autotests.direct.web.api.models.CryptaGoalWeb;
import ru.yandex.autotests.direct.web.api.models.MetrikaGoalWeb;
import ru.yandex.autotests.direct.web.api.models.RetargetingConditionActionResponse;
import ru.yandex.autotests.direct.web.api.models.RetargetingConditionWeb;
import ru.yandex.autotests.directapi.common.api45.RetargetingGoal;
import ru.yandex.autotests.directapi.darkside.Logins;
import ru.yandex.autotests.directapi.darkside.datacontainers.jsonrpc.displaycanvas.ModerationInfo;
import ru.yandex.autotests.directapi.darkside.model.CampaignsType;
import ru.yandex.autotests.directapi.darkside.model.RunBsTransportScriptResponse;
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.darkside.model.bslogs.clientdata.GoalContext;
import ru.yandex.autotests.directapi.model.retargeting.RetargetingGoalType;
import ru.yandex.autotests.directapi.rules.ApiSteps;
import ru.yandex.autotests.directintapi.bstransport.FeatureNames;
import ru.yandex.autotests.directintapi.bstransport.StoriesNames;
import ru.yandex.autotests.irt.testutils.RandomUtils;
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.Step;
import ru.yandex.qatools.allure.annotations.Stories;

import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.junit.Assert.assertThat;
import static org.junit.Assume.assumeThat;
import static ru.yandex.autotests.direct.db.models.jooq.ppc.enums.AdgroupAdditionalTargetingsTargetingMode.filtering;
import static ru.yandex.autotests.direct.db.models.jooq.ppc.enums.AdgroupAdditionalTargetingsTargetingMode.targeting;
import static ru.yandex.autotests.direct.db.models.jooq.ppc.enums.AdgroupAdditionalTargetingsTargetingType.content_categories;
import static ru.yandex.autotests.direct.db.models.jooq.ppc.enums.AdgroupAdditionalTargetingsValueJoinType.all;
import static ru.yandex.autotests.direct.db.models.jooq.ppc.enums.AdgroupAdditionalTargetingsValueJoinType.any;
import static ru.yandex.autotests.directintapi.bstransport.TransportUtils.DEFAULT_METRIKA_GOAL_TIME;

@Aqua.Test
@Tag(TagDictionary.RELEASE)
@Tag(TagDictionary.TRUNK)
@Description("Проверяем транспорт сегментов крипты")
@Features({FeatureNames.RETARGETINGS, FeatureNames.CPM_BANNER})
@Issue("https://st.yandex-team.ru/DIRECT-127041")
@Stories(StoriesNames.RETARGETING_ACTION_CREATION)
public class SendingOfCpmBannerWithContentCategoriesTest {

    private static final String LOGIN = Logins.LOGIN_TRANSPORT;

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

    @Rule
    public DirectRule directRule = DirectRule.defaultRule().as(LOGIN);

    public CreativeBannerRule bannersRule = new CreativeBannerRule(CampaignTypeEnum.TEXT).withUlogin(LOGIN);

    @Rule
    public DirectCmdRule cmdRule = DirectCmdRule.defaultRule().withRules(bannersRule);

    private static int shard;
    private Long pid;
    private Campaign campaign;
    private long metrikaGoalID;
    private int emptyGoalsRetId;
    private Long mixedRetCondId;
    private int mixedRetId;
    private Long emptyGoalsRetCondId;
    private String expectedCategoriesResult
            = "(4294970306:0@983|4294970305:0@983|4294968349:0@982|4294968345:0@982)&"
            + "(4294970300:0@983)&"
            + "(4294970299:0@983)&"
            + "(4294968341:0@982)&"
            + "(4294968338:0@982)";


    private static final long CRYPTA_GOAL_ID_1 = 2499000010L;
    private static final long CRYPTA_GOAL_ID_2 = 2499001100L;

    @BeforeClass
    public static void beforeClass() {
        shard = api.userSteps.clientFakeSteps().getUserShard(LOGIN);
    }

    @Before
    @Step("Подготовка тестовых данных")
    public void init() {
        Long cid = bannersRule.getCampaignId();
        Long creativeId = bannersRule.getCreativeId();
        Long bid = bannersRule.getBannerId();
        pid = bannersRule.getGroupId();

        api.userSteps.campaignFakeSteps().makeNewCampaignReadyForSendingToBS(cid);
        api.userSteps.groupFakeSteps().makeGroupFullyModerated(pid);
        api.userSteps.bannersFakeSteps().makeBannerFullyModerated(bid);

        RetargetingConditionWeb retargetingConditionWeb = new RetargetingConditionWeb()
                .withName("testName" + System.currentTimeMillis())
                .withDescription("testDescription")
                .withType(RetargetingConditionWeb.TypeEnum.INTERESTS)
                .withConditions(emptyList());

        RetargetingConditionActionResponse emptyGoalsCondition =
                directRule.webApiSteps().retargetingSteps()
                        .createRetargetingCondition(retargetingConditionWeb, LOGIN);
        this.emptyGoalsRetCondId = emptyGoalsCondition.getId();
        emptyGoalsRetId = api.userSteps.retargetingSteps().addRetargetingToBanner(LOGIN, bid, emptyGoalsRetCondId.intValue());

        this.mixedRetCondId = createMixedRetCondition(LOGIN);
        mixedRetId = api.userSteps.retargetingSteps().addRetargetingToBanner(LOGIN, bid, mixedRetCondId.intValue());

        DirectJooqDbSteps jooqDbSteps = api.userSteps.getDirectJooqDbSteps().useShard(shard);
        jooqDbSteps.bannersPerformanceSteps()
                .setCreativeStatusModerate(cid, pid, bid, BannersPerformanceStatusmoderate.Yes);
        jooqDbSteps.perfCreativesSteps().setModerateInfo(creativeId,
                new ModerationInfo().generateTestData().toString());
        jooqDbSteps.perfCreativesSteps().setStatusModerate(creativeId, PerfCreativesStatusmoderate.Yes);
        api.userSteps.campaignFakeSteps().setType(cid, CampaignsType.CPM_BANNER);
        jooqDbSteps.adGroupsSteps().setType(pid, PhrasesAdgroupType.cpm_banner);
        jooqDbSteps.bannersSteps().setBannersBannerType(bid, BannersBannerType.cpm_banner);
        jooqDbSteps.adGroupsInternalSteps().addAdGroupAdditionalTargetings(
                new AdgroupAdditionalTargetingsRecord()
                        .setPid(pid)
                        .setTargetingMode(targeting)
                        .setTargetingType(content_categories)
                        .setValueJoinType(any)
                        .setValue("[4294970306, 4294970305, 4294968349, 4294968345]"));
        jooqDbSteps.adGroupsInternalSteps().addAdGroupAdditionalTargetings(
                new AdgroupAdditionalTargetingsRecord()
                        .setPid(pid)
                        .setTargetingMode(filtering)
                        .setTargetingType(content_categories)
                        .setValueJoinType(all)
                        .setValue("[4294970300, 4294970299, 4294968341, 4294968338]"));

        RunBsTransportScriptResponse resp
                = api.userSteps.getDarkSideSteps().getTransportSteps().sendNewCampaign(shard, cid);
        campaign =
                api.userSteps.getDarkSideSteps().getTransportSteps().getClientDataRequestCampaign(resp, 0, cid);

    }

    @After
    @Step("Утилизация тестовых данных")
    public void after() {
        Stream.of( emptyGoalsRetId, mixedRetId).filter(Objects::nonNull).forEach(
                retId -> api.userSteps.retargetingSteps().retargetingDelete(LOGIN, retId));
        Stream.of(emptyGoalsRetCondId, mixedRetCondId).filter(Objects::nonNull).forEach(
                retCondId -> directRule.webApiSteps().retargetingSteps()
                        .deleteRetargetingCondition(singletonList(retCondId), LOGIN));
    }

    public Long createMixedRetCondition(String login) {
        List<RetargetingGoal> segmentGoals
                = api.userSteps.retargetingSteps().getRetargetingGoals(RetargetingGoalType.SEGMENT, login);
        assumeThat("кол-во целей типа сегмент > 0", segmentGoals.size(), greaterThan(0));

        final RetargetingGoal goal = segmentGoals.get(RandomUtils.getRandomInteger(0, segmentGoals.size() - 1));
        metrikaGoalID = goal.getGoalID();
        final Condition metrikaCond = getMetrikaCond(metrikaGoalID);
        RetargetingConditionWeb retargetingConditionWeb = new RetargetingConditionWeb()
                .withName("testName" + System.currentTimeMillis())
                .withDescription("testDescription")
                .withType(RetargetingConditionWeb.TypeEnum.INTERESTS)
                .withConditions(Arrays.asList(
                        getSingleCryptaSegmentCond(CRYPTA_GOAL_ID_1),
                        getSingleCryptaSegmentCond(CRYPTA_GOAL_ID_2),
                        metrikaCond));

        RetargetingConditionActionResponse mixedResult = directRule.webApiSteps().retargetingSteps()
                .createRetargetingCondition(retargetingConditionWeb, login);
        return mixedResult.getId();
    }

    public Condition getSingleCryptaSegmentCond(long cryptaGoalId) {
        return new Condition()
                .withInterestType(Condition.InterestTypeEnum.ALL)
                .withGoals(singletonList(new CryptaGoalWeb()
                        .withId(cryptaGoalId)
                ))
                .withType(Condition.TypeEnum.OR); // only OR type is allowed for crypta conditions
    }

    public Condition getMetrikaCond(long goalID) {
        return new Condition()
                .withGoals(singletonList(
                        new MetrikaGoalWeb()
                                .withTime(DEFAULT_METRIKA_GOAL_TIME)
                                .withId(goalID)
                ))
                .withType(Condition.TypeEnum.ALL);
    }

    @Test
    public void targetingExpressionShouldBeSentInGoalContextExpression() {
        Context context = campaign.getContext(pid);
        assumeThat("в запросе есть группа", context, notNullValue());

        final GoalContext emptyGoalContext = context.getGoalContext((long) emptyGoalsRetId);
        assumeThat("в запросе с ПУСТЫМ списком целей есть GoalContext", emptyGoalContext, notNullValue());

        assertThat("передан корректный GoalContext->{Expression} для пустых ретаргетингов",
                emptyGoalContext.getExpression(), equalTo(expectedCategoriesResult));

        final GoalContext mixedGoalContext = context.getGoalContext((long) mixedRetId);
        assumeThat("в запросе с НЕПУСТЫМ списком целей есть GoalContext", mixedGoalContext, notNullValue());

        String mixedExpectedCategoriesResult
                = metrikaGoalID+":540&(1:0@618|2:0@618)&(194:0@601|194:0@602)&" + expectedCategoriesResult;

        assertThat("передан корректный GoalContext->{Expression} для ретаргетингов с другими целями", mixedGoalContext.getExpression(),
                equalTo(mixedExpectedCategoriesResult));
    }
}
