package old.page;

import org.openqa.selenium.*;

import java.util.List;
import java.util.stream.Collectors;
import java.time.Duration;

/**
 * @author kote
 */
public class AdminGroupPage extends BaseComponentPage {

  private static final String AVAILABLE_TO_ADD_TABLE = "//*[@object-name='AvailableRecordsGrid']";
  private static final String ADDED_TABLE = "//*[@object-name='LinkFieldDialog']";
  private static final String LINKED_ROLE_TABLE = "//*[@object-name='RolesGrid']";
  private static final String GROUP_NAMES_TABLE = "//*[@object-name='Grid']";
  private static final String ROW_BY_NAME = "//div[@role='row']//div[contains(.,'%s')]";
  private static final String CELL_WITH_GROUP_NAME = "//div[@col-id='group$name']//span[contains(text(),'%s')]";
  private static final String ROLE_TO_ADD_BY_NAME = AVAILABLE_TO_ADD_TABLE + ROW_BY_NAME;

  private static final By LINKED_ROLES_GRID = By.xpath(
      LINKED_ROLE_TABLE + "//div[@col-id='role$name']" + EXCLUDE_HEADER);
  private static final By ROLES_AVAILABLE_TO_ADD_GRID = By.xpath(
      AVAILABLE_TO_ADD_TABLE + "//div[@col-id='role$name']" + EXCLUDE_HEADER);
  private static final By ADDED_ROLES_GRID = By.xpath(
      ADDED_TABLE + "//div[@role='gridcell']" + EXCLUDE_HEADER + "[@col-id='role$name']");
  private static final By GROUP_NAMES_GRID = By.xpath(
      GROUP_NAMES_TABLE + "//div[@col-id='role$name']" + EXCLUDE_HEADER);
  private static final By GRID_BODY = By.xpath(
      "//div[@class='ag-center-cols-container']" + "[//div[@role='row']" + EXCLUDE_HEADER + "]");
  private static final By ROLE_TO_ADD_TABLE_HEADER = By.xpath(
      AVAILABLE_TO_ADD_TABLE + "//div[@col-id='role$name']" + AG_HEADER);
  private static final By ADDED_ROLES_TABLE_HEADER = By.xpath(
      ADDED_TABLE + "//div[@col-id='role$name']" + AG_HEADER);
  private static final By GROUP_NAMES_TABLE_HEADER = By.xpath(
      GROUP_NAMES_TABLE + "//div[@col-id='role$name']" + AG_HEADER);
  private static final By LINKED_ROLES_TABLE_HEADER = By.xpath(
      LINKED_ROLE_TABLE + "//div[@col-id='role$name']" + AG_HEADER);

  public AdminGroupPage(WebDriver browser) {
    super(browser, "/#/");
  }

  public void searchGroup(String groupName) {
    waitProgressBarNotVisible();
    waitForVisibilityOf(searchField);
    typeTextOnAdminFilter(searchField, groupName);
    waitForInvisibilityGridDownloader();
  }

  public void chooseGroup(String groupName) {
    searchGroup(groupName);
    getListOfGroupsNamesFromTable(groupName).get(0).click();
    waitProgressBarNotVisible();
    waitForPageLoad();
  }

  public void deleteGroup() {
    waitProgressBarNotVisible();
    clickDeleteButton();
    clickConfirmButton();
  }

  public List<WebElement> getListOfGroupsNamesFromTable(String name) {
    return driver.findElements(By.xpath(String.format(CELL_WITH_GROUP_NAME, name)));
  }

  public List<WebElement> getListOfRolesFromLinkedTable() {
    return driver.findElements(LINKED_ROLES_GRID);
  }

  public List<WebElement> getListOfRolesToAdd() {
    return driver.findElements(ROLES_AVAILABLE_TO_ADD_GRID);
  }

  public boolean isGridEmpty() {
    try {
      waitForInvisibilityOfElementLocated(GRID_BODY, Duration.ofSeconds(5));
      return true;
    }
    catch (TimeoutException t) {
      return false;
    }
  }

  /**
   * Method to avoid parallel test conflicts.
   * <p>
   * If the list of roles available to adding is not empty, checks if the list contains only newly
   * created roles.
   * </p>
   *
   * @param roleNames list retrieved from the database before the parallel test created a role.
   * @return value of {@link Boolean} type.
   * <p>
   * True - if list of roles available for adding is empty, or contains only newly created roles
   * from parallel tests.
   * </p>
   * <p>
   * False - if list of roles available for adding contains values from roleNames list.
   * </p>
   */
  public boolean isAvailableGridEmpty(List<String> roleNames) {
    List<String> possibleParallelAddedRole = getListOfRolesToAdd()
        .stream()
        .map(WebElement::getText)
        .collect(Collectors.toList());

    if (possibleParallelAddedRole.isEmpty()) {
      return true;
    }
    else {
      return roleNames
          .stream()
          .noneMatch(possibleParallelAddedRole::contains);
    }
  }

  public void addRole(String roleName, boolean needSort) {
    if (needSort) {
      waitForVisibilityOfElementLocated(ROLE_TO_ADD_TABLE_HEADER).click();
    }
    By cell = By.xpath(String.format(ROLE_TO_ADD_BY_NAME, roleName));
    waitForVisibilityOfElementLocated(By.xpath(String.format(ROLE_TO_ADD_BY_NAME, roleName))).click();
    waitForPageLoad();
    clickAddButton();
    waitProgressBarNotVisible();
  }

  public void removeRole(String roleName) {
    waitForVisibilityOfElementLocated(By.xpath(String.format(ADDED_TABLE + ROW_BY_NAME, roleName))).click();
    clickRemoveButton();
    waitProgressBarNotVisible();
  }

  public void addAllRoles() {
    clickAddAllButton();
    waitProgressBarNotVisible();
  }

  public void removeAllRoles() {
    clickRemoveAllButton();
    waitProgressBarNotVisible();
  }

  public boolean hasRolesInTable(GroupPageGrids tableName, String... roleNames) {
    String table = tableName == GroupPageGrids.ADDED_ROLES_GRID ? ADDED_TABLE :
        AVAILABLE_TO_ADD_TABLE;
    for (String roleName : roleNames) {
      List<WebElement> roles = driver.findElements(By.xpath(table + String.format(ROW_BY_NAME, roleName)));
      if (roles.isEmpty()) {
        return false;
      }
    }
    return true;
  }

  public List<WebElement> getListOfAddedRoles() {
    return driver.findElements(ADDED_ROLES_GRID);
  }

  public List<WebElement> getListOfGroupNames() {
    return driver.findElements(GROUP_NAMES_GRID);
  }

  public void bindRole(String roleName) {
    waitForVisibilityOf(addButton);
    addRole(roleName, false);
    waitProgressBarNotVisible();
  }

  public void typeAndSelectLevel(String level) {
    typeTextAndSelectOption("access_level", level, level);
  }

  public void changeGroupPageGridSort(GroupPageGrids value) {
    switch (value) {
      case ROLES_TO_ADD_GRID:
        driver.findElement(ROLE_TO_ADD_TABLE_HEADER).click();
        break;
      case LINKED_ROLES_GRID:
        driver.findElement(LINKED_ROLES_TABLE_HEADER).click();
        break;
      case ADDED_ROLES_GRID:
        driver.findElement(ADDED_ROLES_TABLE_HEADER).click();
      case GROUP_NAMES_GRID:
        driver.findElement(GROUP_NAMES_TABLE_HEADER).click();
    }
    waitForPageLoad();
  }

  public enum GroupPageGrids {
    ADDED_ROLES_GRID,
    ROLES_TO_ADD_GRID,
    LINKED_ROLES_GRID,
    GROUP_NAMES_GRID
  }
}
