package ru.yandex.autotests.direct.web.pages.banners.blocks.edit.bem;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;

import com.google.common.collect.Iterables;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;

import ru.yandex.autotests.direct.web.data.banners.CpmTargetingType;
import ru.yandex.autotests.direct.web.data.retargeting.RetargetingConditionItemWeb;
import ru.yandex.autotests.direct.web.objects.banners.BannerPhraseInfoWeb;
import ru.yandex.autotests.direct.web.objects.banners.CpmBannerInfoWeb;
import ru.yandex.autotests.direct.web.objects.banners.InterestsSocDemTargetingInfoWeb;
import ru.yandex.autotests.direct.web.objects.groups.CpmGroupInfoWeb;
import ru.yandex.autotests.direct.web.objects.groups.CpmTargetingInfoWeb;
import ru.yandex.autotests.direct.web.pages.retargeting.blocks.bem.RetargetingGroupBlockBEM;
import ru.yandex.autotests.direct.web.util.DirectWebError;
import ru.yandex.autotests.direct.web.util.JavaScriptActions;
import ru.yandex.autotests.direct.web.util.helpers.TreeHelper;
import ru.yandex.autotests.direct.web.util.testinterfaces.IWebFrom;
import ru.yandex.autotests.irt.testutils.beandiffer2.comparestrategy.defaultcomparestrategy.DefaultCompareStrategies;
import ru.yandex.qatools.htmlelements.annotations.Name;
import ru.yandex.qatools.htmlelements.element.Button;
import ru.yandex.qatools.htmlelements.element.TextInput;
import ru.yandex.qatools.htmlelements.matchers.WebElementMatchers;

import static ch.lambdaj.Lambda.on;
import static com.google.common.collect.Iterables.getLast;
import static java.lang.Long.parseLong;
import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.core.IsEqual.equalTo;
import static ru.yandex.autotests.direct.web.steps.CommonSteps.waitForElement;
import static ru.yandex.autotests.direct.web.util.WebElementsActions.fillTextInput;
import static ru.yandex.autotests.direct.web.util.beanutils.BeanFieldsSetter.inAccordanceWith;
import static ru.yandex.autotests.irt.testutils.allure.TestSteps.assertThat;
import static ru.yandex.autotests.irt.testutils.allure.TestSteps.assumeThat;
import static ru.yandex.autotests.irt.testutils.beandiffer2.BeanDifferMatcher.beanDiffer;
import static ru.yandex.autotests.irt.testutils.beandiffer2.beanfield.BeanFieldPath.newPath;

@FindBy(xpath = ".//div[contains(@class, 'b-edit-group-2 ')]")
public class EditGroupWithCpmBannerBlockBem extends EditGroupWithBannerBlockBEMBase implements IWebFrom<CpmGroupInfoWeb> {
    @Name("Задать метки")
    @FindBy(xpath = ".//button[contains(@class, 'b-group-tags2__switcher')]")
    private Button setTagsButton;

    @Name("Инпут 'Название группы'")
    @FindBy(xpath = ".//span[contains(@class, 'name-input')]//input")
    private TextInput groupName;

    private List<CpmBannerBlockBem> cpmBannerBlockBems;

    @Name("Кнопка 'Добавить объявление'")
    @FindBy(xpath = ".//button[contains(@class, 'b-edit-banner-add2__add-button')]")
    private Button addBannerButton;

    @Name("Название условия ретаргетинга")
    @FindBy(xpath = ".//div[contains(@class, 'b-editable-label__text')]")
    private WebElement targetingNameLabel;

    @Name("Название условия ретаргетинга (инпут)")
    @FindBy(xpath = ".//div[contains(@class, 'b-editable-label__input')]//input")
    private TextInput targetingNameInput;

    @Name("Иконки удаления слов")
    @FindBy(xpath = ".//span[contains(@class, 'b-group-edit-phrase__remove')]")
    private List<WebElement> removeWordsButton;

    @Name("тип таргетинга")
    @FindBy(xpath = ".//div[contains(@class, 'b-edit-group-2__row_type_display-conditions')]")
    private WebElement cpmTargetingType;

    @Name("Название условия показа")
    @FindBy(xpath = ".//div[@class='b-audience-selection__name']")
    private WebElement targetingconditionTitle;

    @Name("Блок 'Соцдем'")
    @FindBy(xpath = ".//div[contains(@class, 'b-segments-group__social-demo')]")
    private WebElement socDem;

    @Name("Группа чекбоксов 'Пол'")
    @FindBy(xpath = ".//div[contains(@class, 'b-crypta-segments__category')][./div[text()='Пол']]")
    private WebElement genderBlock;

    @Name("Группа чекбоксов 'Возраст'")
    @FindBy(xpath = ".//div[contains(@class, 'b-crypta-segments__category')][./div[text()='Возраст']]")
    private WebElement ageBlock;

    @Name("Группа чекбоксов 'Доход'")
    @FindBy(xpath = ".//div[contains(@class, 'b-crypta-segments__category')][./div[text()='Доход']]")
    private WebElement incomeBlock;

    @Name("Группа чекбоксов 'Семейное положение, дети, профессия'")
    @FindBy(xpath = ".//div[contains(@class, 'b-segments-group__family')]")
    private WebElement additionalTargetings;

    @Name("Группа чекбоксов 'Интересы'")
    @FindBy(xpath = ".//div[contains(@class, 'b-segments-group__interests')]")
    private WebElement interestsTargetings;

    @Name("Блок 'Аудитории'")
    @FindBy(xpath = ".//div[contains(@class, 'b-segments-group__metrika i-bem')]")
    private WebElement segments;

    @Name("Список групп 'Аудитории'")
    private List<RetargetingGroupBlockBEM> auditoriesGroups;

    @Name("Кнопки добавления группы сегментов")
    @FindBy(xpath = ".//span[contains(@class, 'b-retargeting-condition-edit__create-group-rule')]")
    private WebElement addRetargetingGroupButton;

    private TreeHelper treeHelper;
    private Map<String, String> interestsNameIdMap;

    @Override
    public void fillParameters(CpmGroupInfoWeb bean) {
        fillTextInput(groupName, bean.getAdGroupName());
        fillBanners(bean.getBanners());
        fillTargetingParameters(bean.getTargeting());
        fillRegionsAndExpectSuccess(bean.getGeo());
        if (bean.getMaxPrice() != null) {
            fillMaxClickPrice(bean.getMaxPrice());
        }
        fillTags(bean.getTags());
    }

    @Override
    public void checkParameters(CpmGroupInfoWeb expectedBean) {
        assertThat("параметры группы соответствуют ожиданию", getFormFieldsAccording(expectedBean),
                beanDiffer(expectedBean)
                        .useCompareStrategy(DefaultCompareStrategies.allFieldsExcept(
                                newPath("banners", "\\d+", "imageAd", "imageAdLoadType"),
                                newPath("maxPrice"))));
    }

    @Override
    public CpmGroupInfoWeb getFormFieldsAccording(CpmGroupInfoWeb expectedBean) {
        return inAccordanceWith(expectedBean).forNew(CpmGroupInfoWeb.class)
                .set("adGroupName", groupName, on(TextInput.class).getText())
                .set("adGroupId", this, on(EditGroupWithCpmBannerBlockBem.class).getAdGroupId())
                .set("banners", this,
                        on(EditGroupWithCpmBannerBlockBem.class).getBanners(expectedBean.getBanners()))
                .set("targeting", this, on(EditGroupWithCpmBannerBlockBem.class)
                        .getTargetingInAccordance(expectedBean.getTargeting()))
                .set("geo", this, on(EditGroupWithCpmBannerBlockBem.class).getRegionIDs())
                .set("tags", this, on(EditGroupWithCpmBannerBlockBem.class).getTags())
                .getActualBean();
    }

    public Long getAdGroupId() {
        return parseLong(getAdGroupId(this.getAttribute("data-bem")));
    }

    public List<CpmBannerInfoWeb> getBanners(List<CpmBannerInfoWeb> expectedBeans) {
        List<CpmBannerInfoWeb> result = new ArrayList<>();
        int minSize = Math.min(expectedBeans.size(), cpmBannerBlockBems.size());
        for (int i = 0; i < minSize; i++) {
            result.add(cpmBannerBlockBems.get(i).getFormFieldsAccording(expectedBeans.get(i)));
        }
        while (expectedBeans.size() > minSize) {
            result.add(null);
            minSize++;
        }
        while (cpmBannerBlockBems.size() > minSize) {
            CpmBannerBlockBem currentBlock = cpmBannerBlockBems.get(minSize - 1);
            result.add(
                    new CpmBannerInfoWeb()
                            .withBannerId(currentBlock.getBannerId())
                            .withHref(currentBlock.getHref())
                            .withImageAd(currentBlock.getImageAd())
                            .withPixel(currentBlock.getPixelValue())
                            .withViewsCounter(currentBlock.getViewscounterValue()));
            minSize++;
        }
        return result;
    }

    public CpmTargetingInfoWeb getTargetingInAccordance(CpmTargetingInfoWeb bean) {
        CpmTargetingInfoWeb result = new CpmTargetingInfoWeb();
        result.setTargetingType(getCurrentTargetingType());
        switch (result.getTargetingType()) {
            case KEYWORDS:
                deserializeKeywordsTargeting(result, bean);
                break;
            case USER_PROFILE:
                deserializeInterestsAndSocDemTargeting(result, bean.getInterestsSocDemTargeting());
        }
        return result;
    }


    public void fillBannerWithId(CpmBannerInfoWeb cpmBannerInfoWeb) {
        Long bannerId = cpmBannerInfoWeb.getBannerId();
        CpmBannerBlockBem currentBlock = getBannerBlock(bannerId)
                .orElseThrow(() -> new DirectWebError("Не найден баннер с Id: " + bannerId));
        currentBlock.setConfig(config);
        currentBlock.fillParameters(cpmBannerInfoWeb);
    }

    private Optional<CpmBannerBlockBem> getBannerBlock(Long bannerId) {
        return cpmBannerBlockBems.stream()
                .filter(bannerBlock -> bannerBlock.getBannerId().equals(bannerId))
                .findFirst();
    }

    private void deserializeInterestsAndSocDemTargeting(CpmTargetingInfoWeb result,
            InterestsSocDemTargetingInfoWeb expectedBean)
    {
        InterestsSocDemTargetingInfoWeb interestsSocDem = new InterestsSocDemTargetingInfoWeb();
        if (expectedBean.getName() != null) {
            interestsSocDem.setName(targetingconditionTitle.getText());
        }
        if (expectedBean.getAge() != null || expectedBean.getGender() != null || expectedBean.getIncome() != null) {
            expandBlockIfNotExpanded(socDem);
            interestsSocDem.setGender(getSelectedCheckBoxesValues(genderBlock));
            interestsSocDem.setAge(getSelectedCheckBoxesValues(ageBlock));
            interestsSocDem.setIncome(getSelectedCheckBoxesValues(incomeBlock));
        }
        if (expectedBean.getAdditionalSocDemParameters() != null) {
            expandBlockIfNotExpanded(additionalTargetings);
            interestsSocDem.setAdditionalSocDemParameters(getSelectedCheckBoxesValues(additionalTargetings));
        }
        if (expectedBean.getInterests() != null) {
            expandBlockIfNotExpanded(interestsTargetings);
            interestsSocDem.setInterests(getSelectedCheckBoxesValues(interestsTargetings));
        }
        if (expectedBean.getRetargetingConditions() != null) {
            expandBlockIfNotExpanded(segments);
            interestsSocDem.setRetargetingConditions(getRetargetingConditions());
        }
        result.setInterestsSocDemTargeting(interestsSocDem);
    }

    private void deserializeKeywordsTargeting(CpmTargetingInfoWeb result, CpmTargetingInfoWeb expectedBean) {
        result.setPhrases(Stream.of(getPhrases()).map(x -> new BannerPhraseInfoWeb().withPhrase(x)).collect(toList()));
        result.setMinusPhrases(getMinusKeywords());
        result.setHierarchicalMultipliersWeb(
                getHierarchicalMultipliersAccording(expectedBean.getHierarchicalMultipliersWeb()));
    }

    private void fillBanners(List<CpmBannerInfoWeb> banners) {
        if (banners == null) {
            return;
        }
        for (int i = 0; i < banners.size(); i++) {
            if (i < cpmBannerBlockBems.size()) {
                editBanner(banners.get(i), i);
            } else {
                addNewBannerToGroup(banners.get(i));
            }
        }
    }

    private void editBanner(CpmBannerInfoWeb bannerInfoWeb, int idx) {
        CpmBannerBlockBem bannerBlockBem = cpmBannerBlockBems.get(idx);
        bannerBlockBem.setConfig(config);
        bannerBlockBem.fillParameters(bannerInfoWeb);
    }

    private void addNewBannerToGroup(CpmBannerInfoWeb bannerInfoWeb) {
        int oldSize = cpmBannerBlockBems.size();
        clickOnAddNewBannerButton();
        assumeThat("добавлен новый баннер", cpmBannerBlockBems.size(), equalTo(oldSize + 1));
        editBanner(bannerInfoWeb, oldSize);
    }

    public void clickOnAddNewBannerButton() {
        addBannerButton.click();
    }

    private void fillTargetingParameters(CpmTargetingInfoWeb targeting) {
        if (targeting == null) {
            return;
        }
        if (getCurrentTargetingType() != targeting.getTargetingType()) {
            List<WebElement> elements = cpmTargetingType.findElements(By.xpath(".//button"));
            assumeThat("возможно изменение типа таргетинга", elements, is(not(empty())));
            new CpmTargetingTypeSelect(elements.get(0)).selectType(targeting.getTargetingType());
        }
        switch (targeting.getTargetingType()) {
            case USER_PROFILE:
                fillInterestsAndSocDem(targeting.getInterestsSocDemTargeting());
                break;
            case KEYWORDS:
                fillKeyWords(targeting);
        }
    }

    private void fillKeyWords(CpmTargetingInfoWeb targeting) {
        if (targeting.getPhrases() != null) {
            fillPhrases(Iterables.toArray(targeting.getPhrases(), BannerPhraseInfoWeb.class));
        }
        if (targeting.getMinusPhrases() != null) {
            fillMinusKeyWordsAndExpectSuccess(Iterables.toArray(targeting.getMinusPhrases(), String.class));
        }
        fillHierarchicalMultipliers(targeting.getHierarchicalMultipliersWeb());
    }

    private void fillInterestsAndSocDem(InterestsSocDemTargetingInfoWeb interestSocDemTargeting) {
        if (interestSocDemTargeting == null) {
            return;
        }
        waitForElement(socDem, 10, WebElementMatchers.isDisplayed());
        String targetingName = interestSocDemTargeting.getName();
        if (targetingName != null) {
            targetingNameLabel.click();
            fillTextInput(targetingNameInput, targetingName);
        }
        expandBlockIfNotExpanded(socDem);
        fillTargetingCheckboxes(genderBlock, interestSocDemTargeting.getGender());
        fillTargetingCheckboxes(ageBlock, interestSocDemTargeting.getAge());
        fillTargetingCheckboxes(incomeBlock, interestSocDemTargeting.getIncome());
        expandBlockIfNotExpanded(additionalTargetings);
        fillTargetingCheckboxes(additionalTargetings, interestSocDemTargeting.getAdditionalSocDemParameters());
        expandBlockIfNotExpanded(interestsTargetings);
        fillInterests(interestSocDemTargeting.getInterests());
        expandBlockIfNotExpanded(segments);
        fillRetargetingConditions(interestSocDemTargeting.getRetargetingConditions());
    }

    private void fillInterests(List<String> interests) {
        if (interests == null) {
            return;
        }
        interests.forEach(this::selectInterest);
    }

    private void fillRetargetingConditions(List<RetargetingConditionItemWeb> retargetingConditions) {
        if (retargetingConditions == null) {
            return;
        }
        int diff = Math.abs(retargetingConditions.size() - auditoriesGroups.size());
        boolean shouldAddCondition = retargetingConditions.size() - auditoriesGroups.size() > 0;
        while (diff-- > 0) {
            if (shouldAddCondition) {
                addRetargetingGroupButton.click();
            } else {
                getLast(auditoriesGroups).clickDelete();
            }
        }
        for (int i = 0; i < retargetingConditions.size(); i++) {
            new JavaScriptActions(config).scrollToElement(auditoriesGroups.get(i).getWrappedElement());
            auditoriesGroups.get(i).fillParameters(retargetingConditions.get(i));
        }
    }

    private List<RetargetingConditionItemWeb> getRetargetingConditions() {
        if (auditoriesGroups.size() == 0) {
            return null;
        }
        return auditoriesGroups.stream().map(RetargetingGroupBlockBEM::getRetargetingConditionItemWeb)
                .collect(toList());
    }

    private void expandBlockIfNotExpanded(WebElement element) {
        if (!element.getAttribute("class").contains("b-expander_open_yes")) {
            element.findElement(By.xpath(".//div[@class='b-expander__header']")).click();
            waitForElement(element, 5, WebElementMatchers.hasAttribute("class",
                    containsString("b-expander_open_yes")));
        }
    }

    private void fillTargetingCheckboxes(WebElement element, List<String> values) {
        if (values == null) {
            return;
        }
        int checkedValuesCount = 0;
        List<WebElement> items = element.findElements(By.xpath(".//div[contains(@class, 'b-segment_js_inited')]"));
        for (WebElement item : items) {
            WebElement checkBox = item.findElement(By.xpath(".//input[@class='checkbox__control']"));
            boolean isItemSelected = !item.findElements(
                    By.xpath(".//span[contains(@class, 'checkbox_checked_yes')]")).isEmpty();
            if (values.contains(item.getText())) {
                if (!isItemSelected) {
                    checkBox.click();
                }
                checkedValuesCount++;
                continue;
            }
            if (isItemSelected) {
                checkBox.click();
            }
        }
        assumeThat("количество отмеченным чекбоксов соответствует ожиданию", checkedValuesCount,
                equalTo(values.size()));
    }

    private List<String> getSelectedCheckBoxesValues(WebElement element) {
        return element.findElements(By.xpath(".//div[contains(@class, 'b-segment_js_inited')]"
                + "[./span[contains(@class, 'checkbox_checked_yes')]]//label"))
                .stream()
                .map(WebElement::getText)
                .collect(toList());
    }

    private CpmTargetingType getCurrentTargetingType() {
        String title = cpmTargetingType.getText();
        int idx = title.indexOf(":");
        String targetingTypeText;
        if (idx == title.length() - 2) {
            targetingTypeText = cpmTargetingType.findElement(By.xpath(".//span[@class='button2__text']")).getText();
        } else {
            targetingTypeText = title.substring(idx + 2);
        }
        return CpmTargetingType.getTypeByTextValue(targetingTypeText.trim());
    }

    private TreeHelper getTreeHelper() {
        if (treeHelper == null) {
            HashMap<String, String> treeMap = new HashMap<>();
            interestsNameIdMap = new HashMap<>();
            String dataBemJson = findElement(By.cssSelector(".b-crypta-interests-collection_js_inited"))
                    .getAttribute("data-bem");

            JsonObject dataBem = new JsonParser().parse(dataBemJson).getAsJsonObject();
            // Раскладываем древо в map, ключ-потомок, значение-родитель
            dataBem.getAsJsonObject("b-crypta-interests-collection")
                    .getAsJsonArray("interests")
                    .forEach(jsonElement -> {
                        JsonObject jsonObject = jsonElement.getAsJsonObject();

                        String name = jsonObject.getAsJsonPrimitive("name").getAsString();
                        String itemId = jsonObject.getAsJsonPrimitive("id").getAsString();
                        String parentId = jsonObject.getAsJsonPrimitive("parent_id").getAsString();

                        treeMap.put(itemId, parentId);
                        interestsNameIdMap.put(name, itemId);
                    });

            treeHelper = new TreeHelper(treeMap);
        }
        return treeHelper;
    }

    private Map<String, String> getInterestsNameIdMap() {
        if (interestsNameIdMap == null) {
            getTreeHelper();
        }
        return interestsNameIdMap;
    }

    private final String TREE_ITEM_XPATH = "//span[contains(@class, 'checkbox_id_%s')]";
    private final String EXPANDER_XPATH = TREE_ITEM_XPATH + "/../preceding-sibling::div/button";

    private void selectInterest(String interestDescription) {
        String interestId = getInterestsNameIdMap().get(interestDescription);
        if (interestId == null) {
            throw new DirectWebError(format("Не найден id инетереса для: " + interestDescription));
        }
        getTreeHelper().getAncestorsFor(interestId)
                .forEach(id -> {
                    if (!"0".equals(id) && !isTreeLeafOpened(id)) {
                        WebElement element = findElement(By.xpath(format(EXPANDER_XPATH, id)));
                        waitForElement(element, 3, WebElementMatchers.isDisplayed());
                        element.click();
                    }
                });
        findElement(By.xpath(format(TREE_ITEM_XPATH, interestId))).click();
    }

    private boolean isTreeLeafOpened(String itemId) {
        return findElement(By.xpath(format(TREE_ITEM_XPATH, itemId) + "/../../.."))
                .getAttribute("class")
                .contains("item_open_yes");
    }
}
