/*
 * Copyright (c) 2012-2018 Red Hat, Inc.
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *   Red Hat, Inc. - initial API and implementation
 */
package org.eclipse.che.workspace.infrastructure.docker.container;

import java.util.Objects;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * This class is used for generation docker container name or parsing important information from
 * docker container name.
 *
 * @author Alexander Andrienko
 */
public class ContainerNameGenerator {
  private static final String NODE_HOST_GROUP = "(/|(/[0-9a-z.-]+/))?";
  private static final String WORKSPACE_ID_GROUP = "(?<workspaceId>workspace[0-9a-z]+)";
  private static final String MACHINE_ID_GROUP = "(?<machineId>machine[0-9a-z]+)";

  private static final String CONTAINER_NAME_REGEX =
      "^" + NODE_HOST_GROUP + WORKSPACE_ID_GROUP + "_" + MACHINE_ID_GROUP + "_.+$";
  private static final Pattern CONTAINER_NAME_PATTERN = Pattern.compile(CONTAINER_NAME_REGEX);

  /**
   * Return generated name for docker container. Method generate name for docker container in
   * format: <br>
   *
   * <p>workspaceId + "_" + machineId + "_" + userName +"_" + machineName <b>Notice: if generated
   * container name contains incorrect symbols for creation docker container, then we skip this
   * symbols</b>
   *
   * @param workspaceId unique workspace id, see more (@link WorkspaceConfig#getId)
   * @param machineId unique machine id
   * @param userName name of the user who is docker container owner
   * @param machineName name of the workspace machine
   */
  public String generateContainerName(
      String workspaceId, String machineId, String userName, String machineName) {
    String containerName = workspaceId + '_' + machineId + '_' + userName + '_' + machineName;
    return containerName.toLowerCase().replaceAll("[^a-z0-9_-]+", "");
  }

  /**
   * Parse machine's {@code containerName} to get information about this container (like
   * workspaceId, machineId). Notice: method doesn't parse information about userName or
   * machineName, because we do not give guarantees about the integrity of this data(see more {@link
   * #generateContainerName(String, String, String, String)}) Notice: container name can contains
   * node host (e.g. "/node-host.dev.box/workspacebbx2_machineic3_user321_ws-machine)
   *
   * @param containerName name of the container
   * @return information about container
   */
  public Optional<ContainerNameInfo> parse(String containerName) {
    Matcher matcher = CONTAINER_NAME_PATTERN.matcher(containerName);
    ContainerNameInfo containerNameInfo = null;
    if (matcher.matches()) {
      String workspaceId = matcher.group("workspaceId");
      String machineId = matcher.group("machineId");
      containerNameInfo = new ContainerNameInfo(workspaceId, machineId);
    }
    return Optional.ofNullable(containerNameInfo);
  }

  /**
   * Class contains information about docker container, which was parsed from docker container name.
   * Usually used {@link #parse(String)}
   */
  public static class ContainerNameInfo {

    private final String workspaceId;
    private final String machineId;

    private ContainerNameInfo(String workspaceId, String machineId) {
      this.workspaceId = workspaceId;
      this.machineId = machineId;
    }

    /** Return machineId of the docker container. */
    public String getMachineId() {
      return machineId;
    }

    /** Return workspaceId of the docker container. */
    public String getWorkspaceId() {
      return workspaceId;
    }

    @Override
    public String toString() {
      return "ContainerNameInfo{"
          + "workspaceId='"
          + workspaceId
          + '\''
          + ", machineId='"
          + machineId
          + '\''
          + '}';
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj) return true;
      if (!(obj instanceof ContainerNameInfo)) return false;
      final ContainerNameInfo other = (ContainerNameInfo) obj;
      return Objects.equals(workspaceId, other.workspaceId)
          && Objects.equals(machineId, other.machineId);
    }

    @Override
    public int hashCode() {
      int hash = 7;
      hash = 31 * hash + Objects.hashCode(workspaceId);
      hash = 31 * hash + Objects.hashCode(machineId);
      return hash;
    }
  }
}
