package playlist.model;

import com.datastax.driver.core.*;
import playlist.exceptions.UserExistsException;
import playlist.exceptions.UserLoginException;

import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.UUID;

/**
 * DataStax Academy Sample Application
 *
 * Copyright 2013 DataStax
 *
 */


public class UserDAO extends CassandraData {

  private String username;
  private String password;
  private SortedSet<String> playlist_names;

  private UserDAO(Row row) {
    username = row.getString("username");
    password = row.getString("password");

    // We do this because we want a sorted set, and Cassandra only returns a regular set
    // the driver gives us a HashLinkedSet. We need to choose our implementation.
    playlist_names = new TreeSet<>();
    playlist_names.addAll(row.getSet("playlist_names", String.class));

  }

  UserDAO(String username, String password) {
    this.password = password;
    this.username = username;
    this.playlist_names = new TreeSet<>();
  }

  /**
   * Static method to add a user.  It generates a new UUID for the user to use as its surrogate key.  It first checks to
   * ensure that the user does not already exist.  If it exists, we throw a UserExistsException
   *
   * @param username new user's username address
   * @param password new user's password
   * @return a UserDAO object
   * @throws UserExistsException
   */
  public static UserDAO addUser(String username, String password) throws UserExistsException {

    String queryText = "INSERT INTO users (username, password) values (?, ?) IF NOT EXISTS";

    PreparedStatement preparedStatement = getSession().prepare(queryText);

    // Because we use an IF NOT EXISTS clause, we get back a result set with 1 row containing 1 boolean column called "[applied]"
    ResultSet resultSet = getSession().execute(preparedStatement.bind(username, password));

    // Determine if the user was inserted.  If not, throw an exception.
    boolean userGotInserted = resultSet.one().getBool("[applied]");

    if (!userGotInserted) {
      throw new UserExistsException();
    }

    // Return the new user so the caller can get the userid
    return new UserDAO(username, password);

  }

  /**
   * Delete the user.  It does not need to check if the user already exists.
   */
  public void deleteUser() {
    String queryText = "DELETE FROM users where username = ?";
    PreparedStatement preparedStatement = getSession().prepare(queryText);
    BoundStatement boundStatement = preparedStatement.bind(username);

    // Delete users with CL = Quorum
    boundStatement.setConsistencyLevel(ConsistencyLevel.QUORUM);
    getSession().execute(boundStatement);

  }


  /**
   * Look up a user by e-mail address and return a UserDAO object for it
   *
   * @param username The username address to search
   * @return  A full UserDAO object
   */
  public static UserDAO getUser(String username) {
    String queryText = "SELECT * FROM users where username = ?";
    PreparedStatement preparedStatement = getSession().prepare(queryText);
    BoundStatement boundStatement = preparedStatement.bind(username);

    Row userRow = getSession().execute(boundStatement).one();

    if (userRow == null) {
      return null;
    }

    return new UserDAO(userRow);  }


  /**
   * This routine retrieves a userDAO, but reads it with a consistency level of quorum
   *
   * @param username username address to search
   * @return a UserDAO object
   */
  private static UserDAO getUserWithQuorum(String username) {

    String queryText = "SELECT * FROM users where username = ?";
    PreparedStatement preparedStatement = getSession().prepare(queryText);
    BoundStatement boundStatement = preparedStatement.bind(username);
    boundStatement.setConsistencyLevel(ConsistencyLevel.QUORUM);
    Row userRow = getSession().execute(boundStatement).one();

    if (userRow == null) {
      return null;
    }

    return new UserDAO(userRow);

  }

  /**
   * The also retrieves a user based on the username address, but also validates its password.  If the password is invalid, a
   * UserLoginException is thrown
   *
   * @param username  username address to search
   * @param password  password to validate
   * @return  a completed UserDAO object
   * @throws UserLoginException
   */
  public static UserDAO validateLogin(String username, String password) throws UserLoginException {

    UserDAO user = getUserWithQuorum(username);
    if (user == null || !user.password.contentEquals(password)) {
      throw new UserLoginException();
    }

    return user;
  }

  public String getUsername() {
    return username;
  }

  public String getPassword() {
    return password;
  }

  public Set<String> getPlaylist_names() {
    return playlist_names;
  }


}
