package com.github.bjoernpetersen.jmusicbot.user;

import java.util.Collections;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.mindrot.jbcrypt.BCrypt;

/**
 * <p>Represents a user.</p>
 *
 * There are two types of users: <ol> <li>Guest/temporary users. Can't have permissions and is
 * dropped when the bot stops.</li> <li>Full users. Can have permissions and are permanent.</li>
 * </ol>
 *
 * <p> A user object can become invalid if the users permissions change or the user is deleted. In
 * that case, {@link #isInvalid()} will return true and other methods will throw an
 * IllegalStateException. </p>
 */
public final class User {

  private boolean invalid;
  @Nonnull
  private final String name;
  @Nullable
  private final String hash;
  @Nullable
  private final String uuid;
  @Nonnull
  private final Set<Permission> permissions;

  /**
   * Creates a full user with a password hash and permissions.
   *
   * @param name the users name
   * @param hash his password hash
   * @param permissions his permissions
   */
  User(@Nonnull String name, @Nonnull String hash, @Nonnull Set<Permission> permissions) {
    this.invalid = false;
    this.name = name;
    this.hash = hash;
    this.uuid = null;
    this.permissions = Collections.unmodifiableSet(permissions);
  }

  /**
   * Creates a guest user authenticated by the given uuid. Can't have permissions.
   *
   * @param name the users name
   * @param uuid a uuid
   */
  User(@Nonnull String name, @Nullable String uuid) {
    this.invalid = false;
    this.name = name;
    this.hash = null;
    this.uuid = uuid;
    this.permissions = Collections.emptySet();
  }

  @Nonnull
  public String getName() {
    if (invalid) {
      throw new IllegalStateException();
    }
    return name;
  }

  @Nonnull
  String getHash() {
    if (invalid || hash == null) {
      throw new IllegalStateException();
    }
    return hash;
  }

  public boolean hasPassword(@Nonnull String password) {
    if (invalid) {
      throw new IllegalStateException();
    }
    return BCrypt.checkpw(password, hash);
  }

  String getUuid() {
    if (invalid || uuid == null) {
      throw new IllegalStateException();
    }
    return uuid;
  }

  public boolean hasUuid(String uuid) {
    if (invalid || this.uuid == null) {
      throw new IllegalStateException();
    }
    return this.uuid.equals(uuid);
  }

  @Nonnull
  public Set<Permission> getPermissions() {
    if (invalid) {
      throw new IllegalStateException();
    }
    return permissions;
  }

  /**
   * Determines whether this is a guest user.
   *
   * @return whether this is a guest
   */
  public boolean isTemporary() {
    return hash == null;
  }

  /**
   * Gets the validity state of this object.
   * If the object is invalid, calls to any other methods result in a IllegalStateException.
   *
   * @return whether this user is invalid
   */
  public boolean isInvalid() {
    return invalid;
  }

  void invalidate() {
    this.invalid = true;
  }
}

@@ -3026,11 +3026,710 @@
 rue;%0A  %7D
+%0A%0A  @Override%0A  public boolean equals(Object o) %7B%0A    if (this == o) %7B%0A      return true;%0A    %7D%0A    if (o == null %7C%7C getClass() != o.getClass()) %7B%0A      return false;%0A    %7D%0A%0A    User user = (User) o;%0A%0A    if (invalid != user.invalid) %7B%0A      return false;%0A    %7D%0A    return name.equals(user.name);%0A  %7D%0A%0A  @Override%0A  public int hashCode() %7B%0A    int result = (invalid ? 1 : 0);%0A    result = 31 * result + name.hashCode();%0A    return result;%0A  %7D%0A%0A  @Override%0A  public String toString() %7B%0A    return %22User%7B%22 +%0A        %22invalid=%22 + invalid +%0A        %22, name='%22 + name + '%5C'' +%0A        %22, hash='%22 + hash + '%5C'' +%0A        %22, uuid='%22 + uuid + '%5C'' +%0A        %22, permissions=%22 + permissions +%0A        '%7D';%0A  %7D
 %0A%7D%0A

Implement toString, equals and hashCode in User