package dev.peerat.backend.repository;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import com.password4j.Password;

import dev.peerat.backend.Configuration;

public class DatabaseAuthRepository extends BaseDatabaseQuery{
	
	private static enum Query{
		
		// REGISTER
		CHECK_PSEUDO_AVAILABLE_QUERY("SELECT * FROM players WHERE pseudo = ?"),
		CHECK_EMAIL_AVAILABLE_QUERY("SELECT * FROM players WHERE email = ?"),
		REGISTER_QUERY(
				"INSERT INTO players (pseudo, email, passwd, firstname, lastname, description, avatar) VALUES (?, ?, ?, ?, ?, ?, ?)"),
		REGISTER_PLAYER_IN_EXISTING_GROUP(
				"INSERT INTO containsGroups (fk_player, fk_group) VALUES (?, (SELECT id_group FROM groups WHERE name = ?));"),

		// LOGIN
		CHECK_PASSWORD("SELECT id_player, passwd FROM players WHERE pseudo=?");
		
		private String request;

		Query(Query parent, String request) {
			this.request = parent.request + request;
		}

		Query(String request) {
			this.request = request;
		}

		public PreparedStatement prepare(BaseDatabaseQuery base) throws SQLException {
			return base.prepare(this.request);
		}

		@Override
		public String toString() {
			return this.request;
		}
	}
	
	private Configuration config;
	
	public DatabaseAuthRepository(ConnectionManager con, Configuration config){
		super(con);
		this.config = config;
	}
	
	/**
	 * Check if a pseudo is available
	 *
	 * @param pseudo The pseudo to check
	 * @return True if the pseudo is available, false if it's already taken
	 */
	public boolean checkPseudoAvailability(String pseudo) {
		return checkAvailability(pseudo, Query.CHECK_PSEUDO_AVAILABLE_QUERY.toString());
	}

	/**
	 * Check if an email is available
	 *
	 * @param email The email to check
	 * @return True if the email is available, false if it's already taken
	 */
	public boolean checkEmailAvailability(String email) {
		return checkAvailability(email, Query.CHECK_EMAIL_AVAILABLE_QUERY.toString());
	}

	private boolean checkAvailability(String queriedString, String correspondingQuery) {
		try {
			PreparedStatement statement = prepare(correspondingQuery);
			statement.setString(1, queriedString);
			ResultSet result = statement.executeQuery();
			return !result.next();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * Register a new user
	 *
	 * @param pseudo      The pseudo of the user
	 * @param email       The email of the user
	 * @param password    The password of the user
	 * @param firstname   The firstname of the user
	 * @param lastname    The lastname of the user
	 * @param description The description of the user
	 * @param sgroup      The group of the user
	 * @param avatar      The avatar of the user
	 * @return True if the user was registered, false if an error occurred
	 */
	public int register(String pseudo, String email, String password, String firstname, String lastname,
			String description, String sgroup, String avatar) {
		try {
			String pass = Password.hash(password).withArgon2().getResult();
			System.out.println("pass("+pass.length()+") "+pass);
			Connection con = ensureConnection();
			con.setAutoCommit(false);
			try (PreparedStatement playerStatement = con.prepareStatement(Query.REGISTER_QUERY.toString(),
					Statement.RETURN_GENERATED_KEYS)) {
				playerStatement.setString(1, pseudo);
				playerStatement.setString(2, email);
				playerStatement.setString(3, Password.hash(password).withArgon2().getResult());
				playerStatement.setString(4, firstname);
				playerStatement.setString(5, lastname);
				playerStatement.setString(6, description);
				playerStatement.setString(7, avatar);
				if (playerStatement.executeUpdate() == 1) {
					ResultSet inserted = playerStatement.getGeneratedKeys();
					if (inserted.next()) {
						int newPlayerId = inserted.getInt(1);
						if (!sgroup.isEmpty()) {
							try (PreparedStatement containsGroupsStatement = con
									.prepareStatement(Query.REGISTER_PLAYER_IN_EXISTING_GROUP.toString())) {
								containsGroupsStatement.setInt(1, newPlayerId);
								containsGroupsStatement.setString(2, sgroup);
								containsGroupsStatement.executeUpdate();
							}
						}
						con.commit();
						con.setAutoCommit(true);
						return newPlayerId;
					}
				}
			} catch (SQLException e) {
				con.rollback();
				con.setAutoCommit(true);
				e.printStackTrace();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return -1;
	}

	/**
	 * Login a user
	 *
	 * @param username The username of the user
	 * @param password The password of the user
	 * @return id the id of the user, -1 if not login successefuly
	 */
	public int login(String username, String password) {
		try {
			ensureConnection();
			PreparedStatement statement = prepare(Query.CHECK_PASSWORD.toString());
			statement.setString(1, username);
			ResultSet result = statement.executeQuery();
			if (result.next()) {
				String hashedPassword = result.getString("passwd");
				if (Password.check(password, hashedPassword).withArgon2())
					return result.getInt("id_player");
			}
		} catch (SQLException e) {
		}
		return -1;
	}

}
