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

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

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.SerpLayoutAdjustmentFieldEnum;
import com.yandex.direct.api.v5.bidmodifiers.SerpLayoutAdjustmentGet;
import org.apache.commons.lang3.StringUtils;
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.tags.TagDictionary;
import ru.yandex.autotests.directapi.apiclient.config.Semaphore;
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.Tag;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.hazelcast.SemaphoreRule;

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("Проверка выдачи при использовании фильтра по SerpLayoutAdjustmentFieldNames")
@RunWith(Parameterized.class)
public class GetBidModifiersFieldNamesSerpLayoutTest {

    private static LogSteps log = LogSteps.getLogger(GetBidModifiersFieldNamesSerpLayoutTest.class);
    private static final String CLIENT = BidModifiersLogins.SINGLE_CLIENT_GET;
    private static Long campaignID;
    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 BidModifiersSelectionCriteriaMap criteria;

    @Parameterized.Parameter(value = 2)
    public List<SerpLayoutAdjustmentFieldEnum> fieldNames;

    @Parameterized.Parameters(name = "{0}")
    public static Collection fieldsValue() {
        prepareBidModifiers();
        Object[][] data = new Object[][]{
                {"Поле serpLayout", new BidModifiersSelectionCriteriaMap().withIds(bmID),
                        Arrays.asList(SerpLayoutAdjustmentFieldEnum.SERP_LAYOUT)},
                {"Поле bidModifier", new BidModifiersSelectionCriteriaMap().withIds(bmID),
                        Arrays.asList(SerpLayoutAdjustmentFieldEnum.BID_MODIFIER)},
                {"Поле enabled", new BidModifiersSelectionCriteriaMap().withIds(bmID),
                        Arrays.asList(SerpLayoutAdjustmentFieldEnum.ENABLED)},
                {"Все поля c Ids", new BidModifiersSelectionCriteriaMap().withIds(bmID),
                        Arrays.asList(SerpLayoutAdjustmentFieldEnum.SERP_LAYOUT,
                                SerpLayoutAdjustmentFieldEnum.BID_MODIFIER,
                                SerpLayoutAdjustmentFieldEnum.ENABLED)},
                {"Все поля с CampaignIds", new BidModifiersSelectionCriteriaMap().withCampaignIds(campaignID),
                        Arrays.asList(SerpLayoutAdjustmentFieldEnum.SERP_LAYOUT,
                                SerpLayoutAdjustmentFieldEnum.BID_MODIFIER,
                                SerpLayoutAdjustmentFieldEnum.ENABLED)}
        };
        return Arrays.asList(data);
    }

    private static void prepareBidModifiers() {
        log.info("Подготовим корректировки ставок");
        api.userSteps.clientFakeSteps().fakeClearClientSpentUnits(CLIENT);
        campaignID = api.userSteps.campaignSteps().addDefaultTextCampaign(CLIENT);
        bmID = api.userSteps.bidModifiersSteps().addBidModifierSerpLayoutToCampaign(campaignID);
    }

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

        assumeThat("получены корректировки ставок", bms, hasSize(greaterThan(0)));
        List<SerpLayoutAdjustmentGet> adjustments =
                bms.stream().map(BidModifierGetItem::getSerpLayoutAdjustment).collect(Collectors.toList());

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

    private void checkFieldNames(SerpLayoutAdjustmentGet item, List<SerpLayoutAdjustmentFieldEnum> fields) {
        List<String> gotFileds = getNonNullFieldsNames(item);
        //exclude field duplicate
        String[] expectedFields = fields.stream().map(SerpLayoutAdjustmentFieldEnum::value).toArray(String[]::new);
        assertThat("вернулся объект с корректным набором запрошенных полей", gotFileds,
                containsInAnyOrder(expectedFields));
    }

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