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

import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;

import com.google.common.base.Preconditions;
import org.apache.commons.lang.StringUtils;
import org.hamcrest.Matcher;
import org.joda.time.DateTime;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.yandex.autotests.direct.cmd.DirectCmdSteps;
import ru.yandex.autotests.direct.utils.config.DirectTestRunProperties;
import ru.yandex.autotests.direct.web.TestEnvironment;
import ru.yandex.autotests.direct.web.data.banners.BannerSearchTypeEnum;
import ru.yandex.autotests.direct.web.helpers.FinanceApiHelper;
import ru.yandex.autotests.direct.web.pages.DirectCommonPage;
import ru.yandex.autotests.direct.web.steps.account.AccountSteps;
import ru.yandex.autotests.direct.web.steps.agencies.AgSearchPageSteps;
import ru.yandex.autotests.direct.web.steps.autopayment.AutoPaymentPageSteps;
import ru.yandex.autotests.direct.web.steps.banners.AddBannerMultiEditPageSteps;
import ru.yandex.autotests.direct.web.steps.banners.SearchBannersPageSteps;
import ru.yandex.autotests.direct.web.steps.campaigns.ShowCampSteps;
import ru.yandex.autotests.direct.web.steps.campaigns.ShowCampsSteps;
import ru.yandex.autotests.direct.web.steps.campaigns.ShowMediaplanSteps;
import ru.yandex.autotests.direct.web.steps.campaigns.retargeting.ShowRetargetingCondPageSteps;
import ru.yandex.autotests.direct.web.steps.clients.ModifyUserPageSteps;
import ru.yandex.autotests.direct.web.steps.clients.ShowClientsSteps;
import ru.yandex.autotests.direct.web.steps.clients.UserSettingsPageSteps;
import ru.yandex.autotests.direct.web.steps.clients.UserUploadDocumentsPageSteps;
import ru.yandex.autotests.direct.web.steps.feeds.ShowFeedsSteps;
import ru.yandex.autotests.direct.web.steps.managers.CopyCampPageSteps;
import ru.yandex.autotests.direct.web.steps.mediaplan.MultiEditMediaplanPage2Steps;
import ru.yandex.autotests.direct.web.steps.mediaplan.MultiEditMediaplanPageSteps;
import ru.yandex.autotests.direct.web.steps.payment.BalanceCheckOutPageSteps;
import ru.yandex.autotests.direct.web.steps.payment.PaymentSteps;
import ru.yandex.autotests.direct.web.steps.payment.YandexMoneySteps;
import ru.yandex.autotests.direct.web.steps.representatives.agency.AddAgRepPageSteps;
import ru.yandex.autotests.direct.web.steps.representatives.agency.AdminPageAgPageSteps;
import ru.yandex.autotests.direct.web.steps.representatives.agency.DeleteAgRepPageSteps;
import ru.yandex.autotests.direct.web.steps.representatives.agency.ManageClientsOfAgencyPageSteps;
import ru.yandex.autotests.direct.web.steps.representatives.agency.ShowAgRepsPageSteps;
import ru.yandex.autotests.direct.web.steps.representatives.agency.SwitchAgChiefPageSteps;
import ru.yandex.autotests.direct.web.steps.representatives.client.AddClRepPageSteps;
import ru.yandex.autotests.direct.web.steps.representatives.client.DeleteClRepPageSteps;
import ru.yandex.autotests.direct.web.steps.representatives.client.ShowClRepsPageSteps;
import ru.yandex.autotests.direct.web.steps.representatives.client.SwitchClChiefPageSteps;
import ru.yandex.autotests.direct.web.steps.smartbanners.SmartBannersPageSteps;
import ru.yandex.autotests.direct.web.steps.statistic.ShowCampStatPageBEMSteps;
import ru.yandex.autotests.direct.web.steps.statistic.ShowCampStatPageSteps;
import ru.yandex.autotests.direct.web.steps.statistic.StatisticsReportWizardSteps;
import ru.yandex.autotests.direct.web.steps.transfer.TransferStepZeroSteps;
import ru.yandex.autotests.direct.web.steps.transfer.TransferSteps;
import ru.yandex.autotests.direct.web.util.DirectWebError;
import ru.yandex.autotests.directapi.model.User;
import ru.yandex.autotests.irt.testutils.allure.AllureUtils;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Step;
import ru.yandex.qatools.allure.webdriver.annotations.WithoutScreenshot;
import ru.yandex.qatools.allure.webdriver.pages.BasePageObject;
import ru.yandex.qatools.allure.webdriver.steps.BaseSteps;
import ru.yandex.qatools.allure.webdriver.steps.WebDriverSteps;

import static java.util.Objects.isNull;
import static java.util.stream.Collectors.toList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.core.IsCollectionContaining.hasItem;
import static ru.yandex.autotests.direct.utils.beans.BeanWrapper.wrap;
import static ru.yandex.autotests.direct.web.TestEnvironment.getDirectWebProperties;
import static ru.yandex.autotests.direct.web.steps.CommonSteps.waitForActionResult;
import static ru.yandex.autotests.direct.web.util.OperationSystemUtils.sleep;
import static ru.yandex.autotests.direct.web.util.URLHelper.getTopLevelDomain;
import static ru.yandex.qatools.htmlelements.matchers.WebElementMatchers.isDisplayed;

public class UserSteps extends BaseSteps {

    private DirectTestRunProperties properties;
    private FinanceApiHelper financeApiHelper;

    private final static Logger LOG = LoggerFactory.getLogger(UserSteps.class);

    public DirectTestRunProperties getProperties() {
        if (properties == null) {
            properties = DirectTestRunProperties.getInstance();
        }
        return properties;
    }

    public BackendSteps byUsingBackend() {
        return new BackendSteps();
    }

    public DirectCmdSteps byUsingNewHttpRequest(User user) {
        DirectCmdSteps steps = new DirectCmdSteps();
        steps.authSteps().authenticate(user);
        return steps;
    }

    public AddressBarSteps inBrowserAddressBar() {
        return getInstance(AddressBarSteps.class, config);
    }

    public AddBannerMultiEditPageSteps onAddNewBannerPage() {
        return AddBannerMultiEditPageSteps.getInstance(AddBannerMultiEditPageSteps.class, config);
    }

    public CommonSteps inOperatingSystem() {
        return BaseSteps.getInstance(CommonSteps.class, config);
    }

    public WebDriverSteps inBrowser() {
        return BaseSteps.getInstance(WebDriverSteps.class, config);
    }

    public ShowRetargetingCondPageSteps onRetargetingPage() {
        return ShowRetargetingCondPageSteps.getInstance(ShowRetargetingCondPageSteps.class, config);
    }

    public ShowCampSteps onShowCampPage() {
        return ShowCampSteps.getInstance(ShowCampSteps.class, config);
    }

    public ShowSearchPageSteps onShowSearchPage() {
        return ShowSearchPageSteps.getInstance(ShowSearchPageSteps.class, config);
    }

    public UserSettingsPageSteps onUserSettingsPage() {
        return getInstance(UserSettingsPageSteps.class, config);
    }

    public MultiEditMediaplanPageSteps onMultiEditMediaplanPage() {
        return MultiEditMediaplanPageSteps.getInstance(MultiEditMediaplanPageSteps.class, config);
    }

    public MultiEditMediaplanPage2Steps onMultiEditMediaplanPage2() {
        return MultiEditMediaplanPage2Steps.getInstance(MultiEditMediaplanPage2Steps.class, config);
    }

    public ShowClRepsPageSteps onShowClRepsPage() {
        return ShowClRepsPageSteps.getInstance(ShowClRepsPageSteps.class, config);
    }

    public AddClRepPageSteps onAddClRepPage() {
        return AddClRepPageSteps.getInstance(AddClRepPageSteps.class, config);
    }

    public AdminPageAgPageSteps onAdminPageAgPage() {
        return AdminPageAgPageSteps.getInstance(AdminPageAgPageSteps.class, config);
    }

    public DeleteClRepPageSteps onDeleteClRepPage() {
        return DeleteClRepPageSteps.getInstance(DeleteClRepPageSteps.class, config);
    }

    public ShowAgRepsPageSteps onShowAgRepsPage() {
        return ShowAgRepsPageSteps.getInstance(ShowAgRepsPageSteps.class, config);
    }

    public AddAgRepPageSteps onAddAgRepPage() {
        return AddAgRepPageSteps.getInstance(AddAgRepPageSteps.class, config);
    }

    public DeleteAgRepPageSteps onDeleteAgRepPage() {
        return DeleteAgRepPageSteps.getInstance(DeleteAgRepPageSteps.class, config);
    }

    public SwitchAgChiefPageSteps onSwitchAgChiefPage() {
        return SwitchAgChiefPageSteps.getInstance(SwitchAgChiefPageSteps.class, config);
    }

    public SwitchClChiefPageSteps onSwitchClChiefPage() {
        return SwitchClChiefPageSteps.getInstance(SwitchClChiefPageSteps.class, config);
    }

    public ModifyUserPageSteps onModifyUserPage() {
        return ModifyUserPageSteps.getInstance(ModifyUserPageSteps.class, config);
    }

    public ManageClientsOfAgencyPageSteps onManageClientsOfAgencyPage() {
        return ManageClientsOfAgencyPageSteps.getInstance(ManageClientsOfAgencyPageSteps.class, config);
    }

    public ShowCampsSteps onShowCampsPage() {
        return ShowCampsSteps.getInstance(ShowCampsSteps.class, config);
    }

    public ShowClientsSteps onShowClientsPage() {
        return ShowClientsSteps.getInstance(ShowClientsSteps.class, config);
    }

    public ShowMediaplanSteps onShowMediaplanPage() {
        return ShowMediaplanSteps.getInstance(ShowMediaplanSteps.class, config);
    }

    public AgSearchPageSteps onAgSearchPage() {
        return AgSearchPageSteps.getInstance(AgSearchPageSteps.class, config);
    }

    public ShowCampStatPageSteps onShowCampStatPage() {
        return ShowCampStatPageSteps.getInstance(ShowCampStatPageSteps.class, config);
    }

    public StatisticsReportWizardSteps onStatisticsReportWizardPage() {
        return ShowCampStatPageBEMSteps.getInstance(StatisticsReportWizardSteps.class, config);
    }

    public ShowFeedsSteps onShowFeedsPage() {
        return ShowFeedsSteps.getInstance(ShowFeedsSteps.class, config);
    }

    public SearchBannersPageSteps onSearchBannersPage() {
        return getInstance(SearchBannersPageSteps.class, config);
    }

    public CopyCampPageSteps onCopyCampPageSteps() {
        return CopyCampPageSteps.getInstance(CopyCampPageSteps.class, config);
    }

    public SmartBannersPageSteps onSmartBannersPage() {
        return getInstance(SmartBannersPageSteps.class, config);
    }

    public DirectCommonPage onDirectCommonPage() {
        return DirectCommonPage.getInstance(DirectCommonPage.class, config);
    }

    public UserUploadDocumentsPageSteps onUserUploadDocumentsPage() {
        return getInstance(UserUploadDocumentsPageSteps.class, config);
    }

    protected User user;

    @Step("Авторизуемся под {0}")
    @Description("Используем запрос к http серверу для получения куков")
    public void authorizeAs(User user) {
        this.user = user;
        Preconditions.checkState(isCmdAndWebDomainsAreSame(), "домен верхнего уровня для cmd и web должны совпадать");

        String authUrl = getDirectWebProperties().getAuthUrl();
        String webTopLevelDomain = getTopLevelDomain(authUrl);

        config.getDriver().get(authUrl);
        LOG.info("Current page is: " + config.getDriver().getCurrentUrl());


        List<Cookie> cookies = getUserCookies();

        checkIsLoggedIn(cookies);
        Optional.ofNullable(getMdaCookie(webTopLevelDomain)).ifPresent(cookies::add);
        Optional.ofNullable(getLanguageCookie(webTopLevelDomain)).ifPresent(cookies::add);
        String cookiesString = wrap(cookies).toString();
        setCustomCookies(cookies);
        AllureUtils.addJsonAttachment("Установленные куки", cookiesString);
        LOG.info("Установленные куки: " + cookiesString);

        inBrowserAddressBar().openShowCampPage(0L);

        Optional.ofNullable(getNoCaptchaCookie(webTopLevelDomain)).ifPresent(config.getDriver().manage()::addCookie);
    }

    public void closeDriver(){
        config.quitDriver();
    }

    @Step("Устанавливаем куки")
    public void setCustomCookies(List<Cookie> cookies) {
        AllureUtils.addJsonAttachment("Устанавливаемые куки", wrap(cookies).toString());
        Predicate<Cookie> cookieIsNotForPasssport = x ->
                !(x.getName().equals("pf") || StringUtils.contains(x.getDomain(), "passport.yandex"));
        cookies.stream().filter(cookieIsNotForPasssport).forEach(config.getDriver().manage()::addCookie);
    }

    @Step("Получаем установленные куки")
    public List<Cookie> getUserCookies() {
        return byUsingNewHttpRequest(user).context().getHttpClientConfig().getCookieStore().getCookies()
                .stream()
                .map(this::convertCookie)
                .collect(toList());
    }

    private void checkIsLoggedIn(List<Cookie> cookies) {
        try {
            Cookie cookieLogin = cookies.stream()
                    .filter(t -> "yandex_login".equals(t.getName())).findFirst().get();
            assertThat("произошла ошибка при попытке залогиниться с логином " + user.getLogin()
                    + " и паролем " + user.getPassword(), cookieLogin.getValue(), equalTo(user.getLogin()));
        } catch (NullPointerException npe) {
            throw new DirectWebError("Не удалось залогиниться с логином " + user.getLogin() + ". " +
                    "Возможно изменился пароль.");
        }
    }

    private boolean isCmdAndWebDomainsAreSame() {
        return StringUtils.equals(getTopLevelDomain(getDirectWebProperties().getAuthUrl()),
                getTopLevelDomain(
                        DirectTestRunProperties.getInstance().getDirectCmdAuthPassportHost()));
    }

    /**
     * При смене языка интерфейс делает запрос к методу https://www.yandex.ru/portal/set/lang, который возвращает куку
     * my, которая всегда начинается с YycCAA после чего идет символ идентификатор языка
     */
    private Cookie getLanguageCookie(String webTopLevelDomain) {
        String lang = TestEnvironment.getDirectWebProperties().getInterfaceLanguage();
        if (isNull(lang)) {
            return null;
        }
        String resultValue = "YycCAA";
        switch (lang) {
            case "ru":
                resultValue += "E";
                break;
            case "en":
                resultValue += "M";
                break;
            case "ua":
                resultValue += "I";
                break;
            case "tr":
                resultValue += "g";
                break;
            default:
                throw new DirectWebError("Указан неизвестный язык интерфейса: " + lang);
        }
        return new Cookie("my", resultValue, ".yandex.".concat(webTopLevelDomain),
                "/", null);
    }


    private Cookie getMdaCookie(String webTopLevelDomain) {
        return "0".equals(getDirectWebProperties().getMda()) ?
                new Cookie("mda", "0", ".yandex".concat(webTopLevelDomain), "/", null) :
                null;
    }

    private Cookie getNoCaptchaCookie(String webTopLevelDomain) {
        return DirectTestRunProperties.getInstance().isDirectCmdAuthNoCaptcha() ?
                new Cookie("do_not_show_captcha", "1", ".yandex.".concat(webTopLevelDomain), "/",null) :
                null;
    }

    private Cookie convertCookie(org.apache.http.cookie.Cookie cookie) {
        Cookie result = new Cookie(cookie.getName(),
                cookie.getValue(),
                cookie.getDomain(),
                cookie.getPath(),
                cookie.getExpiryDate() == null ? DateTime.now().plusDays(1).toDate() : cookie.getExpiryDate(),
                cookie.isSecure());
        LOG.info("converted cookie: " + result.toString());
        return result;
    }

    public void authorizeAs(String login, String password) {
        User user = new User();
        user.setLogin(login);
        user.setPassword(password);
        authorizeAs(user);
    }

    @Step("Проверяем, что на странице есть ошибка {0}")
    public void shouldSeeError(Matcher<String> matcher) {
        waitForActionResult(this::getVisibleErrors, 3, not(empty()));

        List<String> errorTexts = getVisibleErrors().stream()
                .map(WebElement::getText)
                .collect(toList());

        assertThat("найдена ошибка", errorTexts, hasItem(matcher));
    }

    private List<WebElement> getVisibleErrors() {
        return config.getDriver()
                .findElements(By.xpath(".//*[contains(@class, 'error')]"))
                .stream()
                .filter(w -> isDisplayed().matches(w))
                .collect(toList());
    }

    @Step("Устанавливаем тип поиска баннера - {0}, вводим текст для поиска - {1} и нажимаем Искать")
    public void setBannerSearchTypeAndTextAndClickSearchButton(BannerSearchTypeEnum searchType, String searchText) {
        onShowCampPage().clickSearchBanners();
        onShowCampPage().chooseBannerSearchType(searchType);
        onShowCampPage().fillBannerSearchTypeInput(searchText);
        onShowCampPage().clickSearchBannersButton();
        sleep(TimeUnit.SECONDS.toMillis(getProperties().getDirectWebWebdriverImplicityWaitTimeoutSec()));
    }

    public <T extends BasePageObject> T page(Class<T> pageClass) {
        return BasePageObject.getInstance(pageClass, getConfig());
    }

    public FinanceApiHelper withApi() {
        if (financeApiHelper == null) {
            financeApiHelper = new FinanceApiHelper();
        }
        return financeApiHelper;
    }

    public TransferSteps onTransferPage() {
        return getInstance(TransferSteps.class, config);
    }

    public PaymentSteps onPaymentPage() {
        return getInstance(PaymentSteps.class, config);
    }

    public BalanceCheckOutPageSteps onBalanceCheckOutPage() {
        return getInstance(BalanceCheckOutPageSteps.class, config);
    }

    public AccountSteps onAccountPage() {
        return getInstance(AccountSteps.class, config);
    }

    public AutoPaymentPageSteps onAutoPaymentPageSteps() {
        return AutoPaymentPageSteps.getInstance(AutoPaymentPageSteps.class, config);
    }

    public YandexMoneySteps onYandexMoney() {
        return getInstance(YandexMoneySteps.class, config);
    }

    public TransferStepZeroSteps onTransferStepZeroPage() {
        return getInstance(TransferStepZeroSteps.class, config);
    }

    @Step
    @WithoutScreenshot
    public void turnMultiCurrencyOn() {
        JavascriptExecutor javaScriptExecutor = (JavascriptExecutor) config.getDriver();
        javaScriptExecutor.executeScript("(function () {\n" +
                "  document.cookie = \"show_currencies=\" + (document.cookie.indexOf(\"show_currencies=1\") " +
                "== -1 ? 1 : '');\n" +
                "  document.location.reload();\n" +
                "}) ()");
    }
}
