package ru.yandex.autotests.direct.api.bidmodifiers.get.video;


import com.yandex.direct.api.v5.bidmodifiers.BidModifierFieldEnum;
import com.yandex.direct.api.v5.bidmodifiers.BidModifierGetItem;
import com.yandex.direct.api.v5.bidmodifiers.BidModifierLevelEnum;
import com.yandex.direct.api.v5.bidmodifiers.VideoAdjustmentFieldEnum;
import com.yandex.direct.api.v5.bidmodifiers.VideoAdjustmentGet;

import org.apache.commons.lang3.StringUtils;
import org.junit.AfterClass;
import org.junit.ClassRule;
import org.junit.Rule;
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.api.bidmodifiers.BidModifiersFeatures;
import ru.yandex.autotests.direct.api.bidmodifiers.BidModifiersLogins;
import ru.yandex.autotests.direct.utils.ReflectionUtils;
import ru.yandex.autotests.directapi.apiclient.config.Semaphore;
import ru.yandex.autotests.direct.utils.converter.EnumToStringValueConverter;
import ru.yandex.autotests.directapi.model.api5.bidmodifiers.BidModifiersSelectionCriteriaMap;
import ru.yandex.autotests.directapi.model.api5.bidmodifiers.GetRequestMap;
import ru.yandex.autotests.directapi.rules.ApiSteps;
import ru.yandex.autotests.directapi.rules.Trashman;
import ru.yandex.autotests.irt.testutils.allure.LogSteps;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.hazelcast.SemaphoreRule;

import java.lang.reflect.Field;
import java.util.*;

import static ch.lambdaj.Lambda.convert;
import static ch.lambdaj.Lambda.extract;
import static ch.lambdaj.Lambda.on;
import static org.hamcrest.Matchers.*;
import static ru.yandex.autotests.irt.testutils.allure.TestSteps.assertThat;
import static ru.yandex.autotests.irt.testutils.allure.TestSteps.assumeThat;

/**
 * Created by liosha on 16.01.2018.
 */
@Aqua.Test
@Features(BidModifiersFeatures.GET)
@Description("Проверка выдачи при использовании фильтра по VideoAdjustmentFieldNames")
@RunWith(Parameterized.class)
public class GetBidModifiersFieldNamesVideoTest {
    private static LogSteps log = LogSteps.getLogger(GetBidModifiersFieldNamesVideoTest.class);
    private static final String client = BidModifiersLogins.SINGLE_CLIENT_GET;

    private static Long campaignID;
    private static Long groupID;

    private static Long bmID;

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

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

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

    @Parameterized.Parameter(value = 0)
    public String description;

    @Parameterized.Parameter(value = 1)
    public String criteriaName;

    @Parameterized.Parameter(value = 2)
    public BidModifiersSelectionCriteriaMap criteria;

    @Parameterized.Parameter(value = 3)
    public List<VideoAdjustmentFieldEnum> fieldNames;

    @Parameterized.Parameters(name = "criteria = {1}, test = {0}")
    public static Collection fieldsValue() {
        prepareBidModifiers();
        Object[][] data = new Object[][]{
                {"Поле bidModifier", "Id", new BidModifiersSelectionCriteriaMap().withIds(bmID),
                        Arrays.asList(VideoAdjustmentFieldEnum.BID_MODIFIER)},
                {"Поле bidModifier", "adGroupId", new BidModifiersSelectionCriteriaMap().withAdGroupIds(groupID),
                        Arrays.asList(VideoAdjustmentFieldEnum.BID_MODIFIER)},
                {"Поле bidModifier", "campaignId", new BidModifiersSelectionCriteriaMap().withCampaignIds(campaignID),
                        Arrays.asList(VideoAdjustmentFieldEnum.BID_MODIFIER)}
        };
        return Arrays.asList(data);
    }

    private static void prepareBidModifiers() {
        log.info("Подготовим видео корректировки ставок");
        api.userSteps.clientFakeSteps().fakeClearClientSpentUnits(client);
        campaignID = api.userSteps.campaignStepsV5().addDefaultTextCampaign(client);
        groupID = api.userSteps.adGroupsSteps().addDefaultGroup(campaignID);

        bmID = api.userSteps.bidModifiersSteps().addBidModifierVideoToGroup(groupID);
    }

    @Test
    public void filedNamesTest() {
        List<BidModifierGetItem> bms = api.userSteps.bidModifiersSteps().bidModifiersGet(
                new GetRequestMap()
                        .withSelectionCriteria(criteria
                                .withLevels(BidModifierLevelEnum.AD_GROUP))
                        .withFieldNames(BidModifierFieldEnum.ID)
                        .withVideoFieldNames((VideoAdjustmentFieldEnum[]) fieldNames.toArray())
        );

        assumeThat("получены корректировки ставок", bms, hasSize(greaterThan(0)));
        List<VideoAdjustmentGet> videoAdjustments = extract(bms, on(BidModifierGetItem.class).getVideoAdjustment());

        log.info("Проверим набор видео корректировок на наличие запрошенных полей");
        for (VideoAdjustmentGet item : videoAdjustments) {
            checkFieldNames(item, fieldNames);
        }
    }

    private void checkFieldNames(VideoAdjustmentGet item, List<VideoAdjustmentFieldEnum> fields) {
        List<String> gotFileds = getNonNullFieldsNames(item);

        //exclude field duplicates
        HashSet<VideoAdjustmentFieldEnum> fieldsSet = new HashSet<>(fields);
        List<VideoAdjustmentFieldEnum> nonDuplicatedFields = new ArrayList<>(fieldsSet);

        String[] expectedFields = convert(new ArrayList<>(nonDuplicatedFields), new EnumToStringValueConverter())
                .toArray(new String[nonDuplicatedFields.size()]);

        assertThat("вернулся объект с корректным набором запрошенных полей", gotFileds, containsInAnyOrder(expectedFields));
    }

    private List<String> getNonNullFieldsNames(Object object) {
        if (object == null) {
            throw new IllegalArgumentException("Необходимо передать объект для извлечения имен инициализированных полей");
        }
        log.info("Извлечем имена инициализированных полей объекта");
        List<String> nonNullFileds = new ArrayList<>();
        for (Field field : object.getClass().getDeclaredFields()) {
            if (ReflectionUtils.invokeGetter(object, StringUtils.capitalize(field.getName())) != null) {
                nonNullFileds.add(StringUtils.capitalize(field.getName()));
            }
        }
        return nonNullFileds;
    }

    @AfterClass
    public static void putCampaignToRemove(){
        //workaround для корректроного создания кампаний в параметризованном тесте
        api.userSteps.campaignStepsV5().putCampaignToQueue(client, campaignID);
    }
}
