package old.page;

import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.TimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import old.core.BasePage;
import old.util.ExecutionManager;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
import java.time.Duration;

import static org.junit.jupiter.api.Assertions.assertTrue;

public abstract class BaseComponentPage extends BasePage {

  private static final By PROGRESS_BAR = By.xpath("//img[contains(@src, 'progress.gif')]");
  private static final By NGB_MODAL_WINDOW = By.xpath("//ngb-modal-window");
  private static final By LOGIN_HEADER_BY = By.xpath("//h2[contains(.,'Вход')]");
  private static final String SELECTIZE_BY_OBJECT_NAME = "//*[@object-name='%s']";
  private static final By INPUT_IS_ACTIVE = By.xpath("//div[contains(@class, 'input-active')]");
  private static final String DATE_TIME_CREATE_OR_UPDATE =
      "//div[@col-id='%s']//span[contains(text(),'%s')]/parent::span/parent::div/parent::div/following-sibling::div[@col-id='%s']//span";
  private static final By GRID_PROGRESS_BAR = By.xpath(
      "//span[contains(text(), 'Загрузка данных, пожалуйста, подождите.')]");
  protected static final By EMAIL_INPUT_BY = By.name("username");
  protected static final By FIRST_ROW_BY = By.cssSelector(".ag-center-cols-container [row-index='0']");
  protected static final By MODAL_DIALOG_FIRST_ROW_BY = By.cssSelector(
      ".modal-dialog .ag-center-cols-container [row-index='0']");
  protected static final String SELECTIZE_INPUT_BY_OBJECT_NAME = SELECTIZE_BY_OBJECT_NAME + "//input";
  protected static final String INPUT_WITH_SELECTED_OPTION = SELECTIZE_BY_OBJECT_NAME + "//div[contains(.,'%s')]";
  protected static final String OPTION_TO_SELECT = SELECTIZE_BY_OBJECT_NAME + "//div[@data-selectable][contains(.,'%s')]";
  protected static final String SELECTIZE_FULL = SELECTIZE_BY_OBJECT_NAME + "//div[contains(@class, 'full has-items')]";
  protected static final String EXCLUDE_HEADER = "[not(contains(@class,'ag-header'))]";
  protected static final String AG_HEADER = "[contains(@class,'ag-header')]";
  private static final String PAGE_HEADER = "//*[contains(text(),'%s')]";

  //Administration Page
  @FindBy(xpath = "//*[contains(text(),'Фильтр по пользователям')]")
  public WebElement filterPanel;
  @FindBy(xpath = "//*[@object-name='last-name']//input")
  public WebElement surNameField;
  @FindBy(xpath = "//*[@object-name='search']//input")
  protected WebElement searchField;
  @FindBy(xpath = "//*[@object-name='create-button']//button['Создать']")
  protected WebElement createButton;
  @FindBy(xpath = "//*[@object-name='edit-button']//button['Редактировать']")
  protected WebElement editButton;
  @FindBy(xpath = "//*[@object-name='delete-button']//button['Удалить']")
  protected WebElement deleteButton;
  @FindBy(xpath = "//div[@class='modal-dialog']//button[@test-id='yes-btn']")
  protected WebElement confirmButton;
  @FindBy(xpath = "//button[contains(.,'Отменить')]")
  public WebElement cancelButton;

  //Editing/Creating Pages
  @FindBy(xpath = "//*[@object-name='name']//input")
  public WebElement nameField;
  @FindBy(xpath = "//button[contains(.,'Сохранить')]")
  public WebElement saveButton;
  @FindBy(xpath = "//*[@object-name='Add']//button[text()='>']")
  protected WebElement addButton;
  @FindBy(xpath = "//*[@object-name='Remove']//button[text()='<']")
  protected WebElement removeButton;
  @FindBy(xpath = "//*[@object-name='AddAll']//button[text()='>>']")
  protected WebElement addAllButton;
  @FindBy(xpath = "//*[@object-name='RemoveAll']//button[text()='<<']")
  protected WebElement removeAllButton;
  @FindBy(xpath = "//button[contains(.,'Прервать')]")
  private WebElement stopButton;

  final Logger logger = LoggerFactory.getLogger(getClass());
  protected static ExecutionManager EXECUTION_MANAGER = new ExecutionManager();

  public BaseComponentPage(WebDriver browser, String uri) {
    super(browser, uri);
  }

  @Override
  public void waitForPageLoad() {
    super.waitForPageLoad();
    assertTrue(thereIsNoError());
    waitForInvisibilityOfElementLocated(NGB_MODAL_WINDOW);
  }

  @Override
  public void refreshPage() {
    driver.navigate().refresh();
    waitForPageLoad();
  }

  public void waitForPageLoadWithoutCheckingForError() {
    super.waitForPageLoad();
    waitForInvisibilityOfElementLocated(NGB_MODAL_WINDOW);
  }

  public void doubleClick(WebElement element) {
    Actions action = new Actions(driver);
    action.doubleClick(element).perform();
  }

  public void typeText(WebElement input, String text) {
    waitForVisibilityOf(input);
    input.clear();
    input.sendKeys(text);
    waitProgressBarNotVisible();
  }

  protected void typeTextFromJS(WebElement input, String text) {
    input.click();
    input.clear();
    ((JavascriptExecutor) driver).executeScript("arguments[0].value='" + text + "';", input);
  }

  public void typeTextOnAdminFilter(WebElement input, String text) {
    typeTextFromJS(input, text);
    input.sendKeys(" ");
    input.sendKeys(Keys.ENTER);
    blur(input);
  }

  protected String getErrorText(By locator) {
    WebElement errorMessage = waitForVisibilityOfElementLocated(locator);
    return errorMessage.getText();
  }

  public String getErrorTextFromPage() {
    return getErrorText(getErrorBy());
  }

  protected void typeTextAndSelectOption(String objectName, String inputText, String optionText) {
    WebElement selectize = findElement(
        By.xpath(String.format(SELECTIZE_BY_OBJECT_NAME, objectName)));
    waitForElementToBeClickable(selectize).click();
    WebElement input = findElement(
        By.xpath(String.format(SELECTIZE_INPUT_BY_OBJECT_NAME, objectName)));
    input.sendKeys(Keys.BACK_SPACE);
    typeText(input, inputText);
    By optionToSelectBy = By.xpath(String.format(OPTION_TO_SELECT, objectName, optionText));
    waitForVisibilityOfElementLocated(optionToSelectBy).click();
    waitForInvisibilityOfElementLocated(optionToSelectBy);
    waitForVisibilityOfElementLocated(By.xpath(String.format(SELECTIZE_FULL, objectName)));
    waitForVisibilityOfElementLocated(By.xpath(String.format(INPUT_WITH_SELECTED_OPTION, objectName, optionText)));
  }

  public String getLocalDate() {
    return LocalDateTime.now().minusHours(3).format(DateTimeFormatter.ofPattern("dd.MM.yyyy"));
  }

  public WebElement getActionTime(String colId, String name, String сolIdAction) {
    return waitForVisibilityOfElementLocated(By.xpath(String.format
        (DATE_TIME_CREATE_OR_UPDATE, colId, name, сolIdAction)));
  }

  public void waitForInitializationOf(WebElement... elements) {
    long pageLoadStart = System.currentTimeMillis();
    waitForPageLoad();
    logger.info("page loaded in " + (System.currentTimeMillis() - pageLoadStart) + " ms");
    for (WebElement element : elements) {
      long elementVisibilityStart = System.currentTimeMillis();
      waitForVisibilityOf(element);
      logger.info(element + " became visible in " + (System.currentTimeMillis() - elementVisibilityStart) + " ms");
    }
  }

  // ожидается, что последний инпут, по котрому был клик, выбран
  public void waitForInputFieldSelected() {
    waitForNumberOfElementsToBe(INPUT_IS_ACTIVE, 1);
  }

  public static By getProgressBar() {
    return PROGRESS_BAR;
  }

  public static By getLoginHeaderBy() {
    return LOGIN_HEADER_BY;
  }

  public List<String> getWebElementTexts(List<WebElement> elements) {
    return elements.stream()
        .map(WebElement::getText)
        .collect(Collectors.toList());
  }

  protected WebElement findElement(By by) {
    return driver.findElement(by);
  }

  protected List<WebElement> findElements(By by) {
    return driver.findElements(by);
  }

  public void waitForInvisibilityGridDownloader() {
    try {
      waitForVisibilityOfElementLocated(GRID_PROGRESS_BAR, Duration.ofMillis(1000));
      waitForInvisibilityOfElementLocated(GRID_PROGRESS_BAR);
    }
    catch (TimeoutException ignored) {
      logger.info("progress bar not visible");
    }
  }

  protected void blur(WebElement input) {
    ((JavascriptExecutor) driver).executeScript("arguments[0].blur()", input);
    ((JavascriptExecutor) driver).executeScript("document.body.click()");
  }

  public void cancelAction() {
    waitForElementToBeClickable(cancelButton).click();
    waitForPageLoad();
    clickConfirmButton();
  }

  public void clickSaveButton() {
    waitForElementToBeClickable(saveButton).click();
    waitProgressBarNotVisible();
    waitForPageLoad();
  }

  public void clickEditButton() {
    waitForElementToBeClickable(editButton).click();
    waitForPageLoad();
  }

  public void clickCreateButton() {
    waitForElementToBeClickable(createButton).click();
    waitForPageLoad();
  }

  public void clickDeleteButton() {
    waitForElementToBeClickable(deleteButton).click();
    waitProgressBarNotVisible();
  }

  public void clickConfirmButton() {
    waitForElementToBeClickable(confirmButton).click();
    waitProgressBarNotVisible();
  }

  public void clickAddButton() {
    waitForElementToBeClickable(addButton).click();
    waitForPageLoad();
  }

  public void clickRemoveButton() {
    waitForElementToBeClickable(removeButton).click();
    waitForPageLoad();
  }

  public void clickAddAllButton() {
    waitForElementToBeClickable(addAllButton).click();
    waitForPageLoad();
  }
  public void clickRemoveAllButton() {
    waitForElementToBeClickable(removeAllButton).click();
    waitForPageLoad();
  }

  public void clickCancelButton() {
    waitForElementToBeClickable(cancelButton).click();
  }

  public void clickStopButton() {
    waitForElementToBeClickable(stopButton).click();
    waitProgressBarNotVisible();
  }

  public void searchUser(String userSurName) {
    filterPanel.click();
    waitForPageLoad();
    typeTextOnAdminFilter(surNameField, userSurName);
    waitForInvisibilityGridDownloader();
  }

  public void checkTitle(String labelName) {
    waitForVisibilityOfElementLocated(By.xpath(String.format(PAGE_HEADER, labelName)));
  }
}