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


import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;

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.IncomeGradeAdjustmentFieldEnum;
import com.yandex.direct.api.v5.bidmodifiers.IncomeGradeAdjustmentGet;
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.direct.utils.converter.EnumToStringValueConverter;
import ru.yandex.autotests.direct.utils.tags.TagDictionary;
import ru.yandex.autotests.directapi.apiclient.config.Semaphore;
import ru.yandex.autotests.directapi.model.api5.bidmodifiers.BidModifierAddMap;
import ru.yandex.autotests.directapi.model.api5.bidmodifiers.BidModifiersSelectionCriteriaMap;
import ru.yandex.autotests.directapi.model.api5.bidmodifiers.GetRequestMap;
import ru.yandex.autotests.directapi.model.api5.bidmodifiers.IncomeGradeAdjustmentMap;
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.Tag;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.hazelcast.SemaphoreRule;

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

@Aqua.Test
@Features(BidModifiersFeatures.GET)
@Tag(TagDictionary.TRUNK)
@Description("Проверка выдачи при использовании фильтра по IncomeGradeAdjustmentFieldNames")
@RunWith(Parameterized.class)
public class GetBidModifiersFieldNamesIncomeGradeTest {
    private static LogSteps log = LogSteps.getLogger(GetBidModifiersFieldNamesIncomeGradeTest.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<IncomeGradeAdjustmentFieldEnum> fieldNames;

    @Parameterized.Parameters(name = "criteria = {1}, test = {0}")
    public static Collection fieldsValue() {
        prepareBidModifiers();
        Object[][] data = new Object[][]{
                {"Поле grade", "Id", new BidModifiersSelectionCriteriaMap().withIds(bmID),
                        Arrays.asList(IncomeGradeAdjustmentFieldEnum.GRADE)},
                {"Поле enabled", "Id", new BidModifiersSelectionCriteriaMap().withIds(bmID),
                        Arrays.asList(IncomeGradeAdjustmentFieldEnum.ENABLED)},
                {"Поле bidModifier", "Id", new BidModifiersSelectionCriteriaMap().withIds(bmID),
                        Arrays.asList(IncomeGradeAdjustmentFieldEnum.BID_MODIFIER)},
                {"Все поля", "Id", new BidModifiersSelectionCriteriaMap().withIds(bmID),
                        Arrays.asList(IncomeGradeAdjustmentFieldEnum.values())},

                {"Все поля", "adGroupId", new BidModifiersSelectionCriteriaMap().withAdGroupIds(groupID),
                        Arrays.asList(IncomeGradeAdjustmentFieldEnum.values())},

                {"Все поля", "campaignId",
                        new BidModifiersSelectionCriteriaMap().withCampaignIds(campaignID.longValue()),
                        Arrays.asList(IncomeGradeAdjustmentFieldEnum.values())}
        };
        return Arrays.asList(data);
    }

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

        bmID = api.userSteps.bidModifiersSteps().bidModifiersAdd(new BidModifierAddMap()
                .withAdGroupId(groupID)
                .withIncomeGradeAdjustments(new IncomeGradeAdjustmentMap()
                        .withDefaultBidModifier()
                        .withDefaultGrade())).get(0);
    }

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

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

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

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

        //exclude field duplicates
        HashSet<IncomeGradeAdjustmentFieldEnum> fieldsSet = new HashSet<>(fields);
        List<IncomeGradeAdjustmentFieldEnum> 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.campaignSteps().putCampaignToQueue(client, campaignID);
    }
}
