package ru.yandex.autotests.direct.web.steps.campaigns.timetarget;


import java.util.LinkedList;
import java.util.List;

import org.hamcrest.Matcher;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import ru.yandex.autotests.direct.web.data.timetarget.TimeZoneGroups;
import ru.yandex.autotests.direct.web.data.timetarget.TimeZones;
import ru.yandex.autotests.direct.web.pages.ExtendedHtmlElement;
import ru.yandex.autotests.direct.web.pages.campaigns.timetarget.TimeTargetParametersDaysTable;
import ru.yandex.autotests.direct.web.pages.campaigns.timetarget.TimeTargetPopupBlock;
import ru.yandex.autotests.direct.web.pages.campaigns.timetarget.elements.DaySquareBEM;
import ru.yandex.qatools.allure.annotations.Step;
import ru.yandex.qatools.allure.webdriver.steps.BaseSteps;
import ru.yandex.qatools.htmlelements.element.CheckBox;
import ru.yandex.qatools.htmlelements.element.HtmlElement;
import ru.yandex.qatools.htmlelements.loader.HtmlElementLoader;

import static ch.lambdaj.Lambda.extract;
import static ch.lambdaj.Lambda.on;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;

/**
 * @author Roman Kuhta (kuhtich@yandex-team.ru)
 */
public class TimeTargetSteps extends BaseSteps {
    private TimeTargetParametersDaysTable timeTargetParametersDaysTable;

    @Step
    public void selectFullDays(int... days) {
        TimeTargetParametersDaysTable TimeTargetParametersDaysTable = load(TimeTargetParametersDaysTable.class);
        for (int day : days)
            TimeTargetParametersDaysTable.getDayCheckBox(day).select();
    }

    private <T extends HtmlElement> T load(Class<T> clazz) {
        T block = HtmlElementLoader.create(clazz, config.getDriver());
        if (ExtendedHtmlElement.class.isAssignableFrom(clazz)) {
            ((ExtendedHtmlElement)block).setConfig(config);
        }
        return block;
    }

    @Step
    public void shouldSeeErrorWithText(String errorText) {
        assertThat("неверное сообщение об ошибке",
                load(TimeTargetPopupBlock.class).getErrorMessage().getText(), equalTo(errorText));
    }

    @Step
    public void timeTargetTableShouldMatch(Integer[][] matrix) {
        TimeTargetParametersDaysTable TimeTargetParametersDaysTable = load(TimeTargetParametersDaysTable.class);
        Integer[][] res = new Integer[7][24];
        for (int day = 1; day <= 7; day++) {
            for (int hour = 0; hour <= 23; hour++) {
                Integer value = TimeTargetParametersDaysTable.getTimeTargetDay(day, hour).getCoefficient();
                res[day - 1][hour] = value == 0 ? null : value;
            }
        }
        assertThat("таблица верменного таргетинга не совпадает с ожидаемой", res, equalTo(matrix));
    }

    @Step
    public void setTimeTargetParameters(Integer[][] matrix) {
        TimeTargetPopupBlock timeTargetParameters = load(TimeTargetPopupBlock.class);
        TimeTargetParametersDaysTable timeTargetParametersDaysTable = load(TimeTargetParametersDaysTable.class);
        boolean isBidManagementMode = false;
        for (Integer day = 1; day <= 7; day++) {
            for (Integer hour = 0; hour <= 23; hour++) {
                Integer coefToSet = matrix[day - 1][hour];

                if (coefToSet == null)
                    coefToSet = 0;

                if (coefToSet != 0 && coefToSet != 200 && !isBidManagementMode) {
                    selectCoefManagingSwitcher();
                    isBidManagementMode = true;
                }

                DaySquareBEM daySquare = timeTargetParametersDaysTable.getTimeTargetDay(day, hour);
                if (daySquare.getCoefficient() != coefToSet) {
                    if (isBidManagementMode && !coefToSet.equals(timeTargetParameters.getSelectedCoefValue())) {
                        selectCoefficientValue(coefToSet);
                    }
                    daySquare.click();
                }
            }
        }
    }

    @Step("Устанавливаем значение коэффициента")
    public void selectCoefficientValue(int value) {
        load(TimeTargetPopupBlock.class).selectCoefficientValue(value);
    }

    @Step("Кликаем сохранить")
    public void clickOk() {
        TimeTargetPopupBlock timeTarget = load(TimeTargetPopupBlock.class);
        timeTarget.getSaveButton().click();
    }

    @Step("Кликаем по кнопке 'Отмена'")
    public void clickCancel() {
        TimeTargetPopupBlock timeTarget = load(TimeTargetPopupBlock.class);
        timeTarget.getCancelButton().click();
    }

    @Step
    public void selectCoefManagingSwitcher() {
        TimeTargetPopupBlock timeTargetParameters = load(TimeTargetPopupBlock.class);
        timeTargetParameters.getCoefManagingSwitcher().select();
    }

    @Step
    public void deselectCoefManagingSwitcher() {
        load(TimeTargetPopupBlock.class).getCoefManagingSwitcher().deselect();
    }

    @Step
    public void selectConsiderWorkingHolidays() {
        load(TimeTargetPopupBlock.class).getConsiderWorkingHolidays().select();
    }

    @Step
    public void deselectConsiderWorkingHolidays() {
        load(TimeTargetPopupBlock.class).getConsiderWorkingHolidays().deselect();
    }

    @Step
    public void considerWorkingHolidaysShould(Matcher<CheckBox> matcher) {
        assertThat("неверное значение чекбокса учета рабочих выходных",
                load(TimeTargetPopupBlock.class).getConsiderWorkingHolidays(), matcher);
    }

    @Step
    public void selectConsiderHolidays() {
        load(TimeTargetPopupBlock.class).getConsiderHolidays().select();
    }

    @Step
    public void deselectConsiderHolidays() {
        load(TimeTargetPopupBlock.class).getConsiderHolidays().deselect();
    }

    @Step
    public void clickDontShowOnHolidays() {
        load(TimeTargetPopupBlock.class).setDontShowOnHolidays();
    }

    @Step
    public void selectHolidaysShowTimeFrom(String from) {
        TimeTargetPopupBlock timeTarget = load(TimeTargetPopupBlock.class);
        timeTarget.getShowOnHolidaysFrom().selectByVisibleText(from);
    }

    @Step
    public void selectHolidaysShowTimeTo(String to) {
        TimeTargetPopupBlock timeTarget = load(TimeTargetPopupBlock.class);
        timeTarget.getShowOnHolidaysTo().selectByVisibleText(to);
    }

    @Step
    public void selectHolidaysShowTime(String from, String to) {
        selectHolidaysShowTimeFrom(from);
        selectHolidaysShowTimeTo(to);
    }

    @Step
    public void deselectFullDays(int... days) {
        TimeTargetParametersDaysTable TimeTargetParametersDaysTable = load(TimeTargetParametersDaysTable.class);
        for (int day : days)
            TimeTargetParametersDaysTable.getDayCheckBox(day).deselect();
    }

    @Step
    public void selectFullHours(int... hours) {
        TimeTargetParametersDaysTable TimeTargetParametersDaysTable = load(TimeTargetParametersDaysTable.class);
        for (int hour : hours)
            TimeTargetParametersDaysTable.getHourCheckBox(hour).select();
    }

    @Step
    public void deselectFullHours(int... hours) {
        TimeTargetParametersDaysTable TimeTargetParametersDaysTable = load(TimeTargetParametersDaysTable.class);
        for (int hour : hours)
            TimeTargetParametersDaysTable.getHourCheckBox(hour).deselect();
    }

    @Step
    public void fullHoursShouldMatch(boolean value, int... hours) {
        TimeTargetParametersDaysTable TimeTargetParametersDaysTable = load(TimeTargetParametersDaysTable.class);
        boolean res;
        for (int hour : hours) {
            res = TimeTargetParametersDaysTable.getHourCheckBox(hour).isSelected();
            assertThat("неверное значение чекбокса выбора часа " + hour,
                    res, is(equalTo(value)));
        }
    }

    @Step
    public void fullDaysShouldMatch(boolean value, int... days) {
        TimeTargetParametersDaysTable TimeTargetParametersDaysTable = load(TimeTargetParametersDaysTable.class);
        boolean res;
        for (int day : days) {
            res = TimeTargetParametersDaysTable.getDayCheckBox(day).isSelected();
            assertThat("неверное значение чекбокса выбора дня " + day,
                    res, is(equalTo(value)));
        }
    }

    @Step
    public void openWorkingHolidaysHelp() {
        TimeTargetPopupBlock timeTarget = load(TimeTargetPopupBlock.class);
        timeTarget.getConsiderWorkingHolidaysHelp().click();
        timeTarget.getOpenInNewWindowButton().click();
    }

    @Step
    public void openHolidaysHelp() {
        TimeTargetPopupBlock timeTarget = load(TimeTargetPopupBlock.class);
        timeTarget.getHolidaysHelp().click();
        timeTarget.getOpenInNewWindowButton().click();
    }

    @Step
    public void selectHolidayCoefficient(String value) {
        load(TimeTargetPopupBlock.class).getHolidayCoef().selectByVisibleText(value);
    }

    @Step
    public void selectedHolidayCoefficientShouldBe(String value) {
        assertThat("неверное значение коэффициента в праздничные дни",
                load(TimeTargetPopupBlock.class).getHolidayCoef().getSelectedText(),
                equalTo(value));
    }

    @Step
    public void considerHolidayShowTimeShouldBe(String from, String to) {
        TimeTargetPopupBlock targetParameters = load(TimeTargetPopupBlock.class);
        assertThat("неверное значение времени начала показа в праздничные дни",
                targetParameters.getShowOnHolidaysFrom().getSelectedText(),
                equalTo(from));
        assertThat("неверное значение времени окончания показа в праздничные дни",
                targetParameters.getShowOnHolidaysTo().getSelectedText(),
                equalTo(to));
    }

    @Step
    public void clickDaySquare(int day, int hour) {
        if (timeTargetParametersDaysTable == null)
            timeTargetParametersDaysTable = load(TimeTargetParametersDaysTable.class);
        timeTargetParametersDaysTable.getTimeTargetDay(day, hour).click();
    }

    @Step
    public void daySquareCoefficientShouldBe(int day, int hour, int coefficient) {
        if (timeTargetParametersDaysTable == null)
            timeTargetParametersDaysTable = load(TimeTargetParametersDaysTable.class);
        assertThat("неверное значение коэффициента для элемента [" + day + "," + hour + "]",
                timeTargetParametersDaysTable.getTimeTargetDay(day, hour).getCoefficient(),
                equalTo(coefficient));
    }

    @Step
    public void setTimeZoneParameters(TimeZoneGroups group, TimeZones timeZone) {
        TimeTargetPopupBlock timeTarget = load(TimeTargetPopupBlock.class);
        timeTarget.setTimeZoneGroup(group.toString());
        timeTarget.setTimeZone(timeZone.getValue());
    }

    @Step
    public void clickAllHoursLink() {
        load(TimeTargetParametersDaysTable.class).getQuickSelectAllHours().click();
    }

    @Step
    public void clickWorkingHoursLink() {
        load(TimeTargetParametersDaysTable.class).getQuickSelectWorkingHours().click();
    }

    @Step
    public void workingHoursCountShouldMatch(int count) {
        TimeTargetParametersDaysTable daysTable = load(TimeTargetParametersDaysTable.class);
        int obtained;
        try {
            obtained = Integer.parseInt(daysTable.getWorkingHoursCounter().getText());
        } catch (NumberFormatException ex) {
            throw new RuntimeException("Значение счетчика рабочих часов некорректно", ex);
        }
        assertThat("неверное количество выбранных рабочих часов", obtained, equalTo(count));
    }

    @Step
    public void workingHoursCounterShouldBeWithError() {
        TimeTargetParametersDaysTable daysTable = load(TimeTargetParametersDaysTable.class);
        assertThat("счетчик рабочих часов не пометился ошибочным",
                daysTable.getWorkingHoursCounterContainer().getAttribute("class"), containsString("error_yes"));
    }

    @Step
    public void workingHoursCounterShouldBeWithoutError() {
        TimeTargetParametersDaysTable daysTable = load(TimeTargetParametersDaysTable.class);
        assertThat("счетчик рабочих часов пометился ошибочным",
                daysTable.getWorkingHoursCounterContainer().getAttribute("class"), not(containsString("error_yes")));
    }

    @Step
    public void shouldNotSeeTimeTargetPopup() {
        assertThat("окно редактирования временного таргетинга не закрылось",
                load(TimeTargetPopupBlock.class).isDisplayed(), is(false));
    }

    @Step
    public void considerHolidaysShowTimeFromShouldMatch(LinkedList<String> values) {
        List<WebElement> options = load(TimeTargetPopupBlock.class).getShowOnHolidaysFrom().getOptions();
        assertThat("неверные значения списка времени начала показа в праздничные дни",
                extract(options, on(WebElement.class).getAttribute("text")).toArray(), arrayContaining(values.toArray()));
    }

    @Step
    public void considerHolidaysShowTimeToShouldMatch(LinkedList<String> values) {
        List<WebElement> options = load(TimeTargetPopupBlock.class).getShowOnHolidaysTo().getOptions();
        assertThat("неверные значения списка времени окончания показа в праздничные дни",
                extract(options, on(WebElement.class).getAttribute("text")).toArray(), arrayContaining(values.toArray()));
    }
}
