package dev.peerat.backend.repository;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.password4j.Password;

import dev.peerat.backend.Configuration;
import dev.peerat.backend.model.Player;
import dev.peerat.backend.model.Puzzle;

public class DatabasePlayerRepository extends BaseDatabaseQuery{
	
	public static String GET_PLAYER_RANK(){
		return Query.GET_PLAYER_RANK.request;
	}
	
	private static enum Query{
		
		// PLAYERS
		GET_PLAYER_SIMPLE("SELECT pseudo, email, firstname, lastname, description FROM players WHERE id_player = ?"),
		GET_PLAYER_EMAIL("SELECT id_player FROM players WHERE email = ?"),
		GET_PLAYER_PSEUDO("SELECT * FROM players WHERE pseudo = ?"),
		GET_PLAYER_DETAILS("SELECT p.*, g.*\r\n"
				+ "FROM players p\r\n"
				+ "LEFT OUTER JOIN containsGroups cg ON p.id_player = cg.fk_player\r\n"
				+ "LEFT OUTER JOIN groups g ON cg.fk_group = g.id_group\r\n"
				+ "LEFT OUTER JOIN completions c on p.id_player = c.fk_player\r\n"
				+ "WHERE "),
		GET_PLAYER_DETAILS_BY_ID(GET_PLAYER_DETAILS, " p.id_player = ? GROUP BY g.name ORDER BY g.fk_chapter, g.fk_puzzle;"),
		GET_PLAYER_DETAILS_BY_PSEUDO(GET_PLAYER_DETAILS, "p.pseudo = ? GROUP BY g.name ORDER BY g.fk_chapter, g.fk_puzzle;"),
		GET_PLAYER_COMPLETIONS("select c.*, p.name from completions c left join puzzles p on c.fk_puzzle = p.id_puzzle where fk_player = ?;"),
		GET_PLAYER_RANK("SELECT * FROM (SELECT fk_player, RANK() OVER(ORDER BY SUM(score) DESC) rank FROM completions c LEFT JOIN puzzles puzz on puzz.id_puzzle = c.fk_puzzle LEFT JOIN chapters chap on chap.id_chapter = puzz.fk_chapter LEFT JOIN players p ON p.id_player = c.fk_player WHERE chap.id_chapter > 1 GROUP BY fk_player ORDER BY rank) AS ranks WHERE ranks.fk_player = ?;"),
		UPDATE_PLAYER_INFO("UPDATE players SET pseudo = ?, email = ?, firstname = ?, lastname = ? WHERE id_player = ?"),
		UPDATE_PLAYER_PASSWORD("UPDATE players SET passwd = ? WHERE id_player = ?");		
		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 DatabasePlayerRepository(ConnectionManager con, Configuration config){
		super(con);
		this.config = config;
	}
	
	public Player getPlayer(int idPlayer) {
		try {
			PreparedStatement completionsStmt = Query.GET_PLAYER_SIMPLE.prepare(this);
			completionsStmt.setInt(1, idPlayer);
			ResultSet result = completionsStmt.executeQuery();
			if (result.next()) {
				return makePlayer(result, idPlayer);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public int getPlayerId(String email){
		try {
			ensureConnection();
			PreparedStatement completionsStmt = Query.GET_PLAYER_EMAIL.prepare(this);
			completionsStmt.setString(1, email);
			ResultSet result = completionsStmt.executeQuery();
			if (result.next()) {
				return result.getInt("id_player");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return -1;
	}
	
	public boolean updatePseudo(int id, Player player, String pseudo){
		try{
			PreparedStatement statment = Query.GET_PLAYER_PSEUDO.prepare(this);
			statment.setString(1, pseudo);
			ResultSet result = statment.executeQuery();
			if(result.next()) return false;
			statment = Query.UPDATE_PLAYER_INFO.prepare(this);
			statment.setString(1, pseudo);
			statment.setString(2, player.getEmail());
			statment.setString(3, player.getFirstname());
			statment.setString(4, player.getLastname());
			statment.setInt(5, id);
			return statment.executeUpdate() > 0;
		}catch(Exception e){
			e.printStackTrace();
		}
		return false;
	}
	
	public void updateProfile(int id, Player player, String lastname, String firstname){
		try{
			PreparedStatement statment = Query.UPDATE_PLAYER_INFO.prepare(this);
			statment.setString(1, player.getPseudo());
			statment.setString(2, player.getEmail());
			statment.setString(3, firstname);
			statment.setString(4, lastname);
			statment.setInt(5, id);
			statment.executeUpdate();
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	
	public void updatePassword(int id, String password){
		try{
			PreparedStatement statment = Query.UPDATE_PLAYER_PASSWORD.prepare(this);
			statment.setString(1, Password.hash(password).withArgon2().getResult());
			statment.setInt(2, id);
			statment.executeUpdate();
		}catch(Exception e){
			e.printStackTrace();
		}
	}

	public Player getPlayerDetails(int idPlayer) {
		return getPlayerDetails(idPlayer, null);
	}

	public Player getPlayerDetails(String pseudoPlayer) {
		return getPlayerDetails(-1, pseudoPlayer);
	}

	private Player getPlayerDetails(int id, String pseudo) {
		try {
			ensureConnection();
			PreparedStatement completionsStmt;
			if (pseudo != null) {
				completionsStmt = Query.GET_PLAYER_DETAILS_BY_PSEUDO.prepare(this);
				completionsStmt.setString(1, pseudo);
			} else {
				completionsStmt = Query.GET_PLAYER_DETAILS_BY_ID.prepare(this);
				completionsStmt.setInt(1, id);
			}
			ResultSet result = completionsStmt.executeQuery();
			Player player = null;
			while (result.next()) {
				if (player == null) {
					id = result.getInt("id_player");
					player = makePlayer(result, id);
					completionsStmt = prepare(DatabaseBadgeRepository.GET_BADGES_OF_PLAYER());
					completionsStmt.setInt(1, id);
					ResultSet resultBadges = completionsStmt.executeQuery();
					while (resultBadges.next()) {
						player.addBadge(makeBadge(resultBadges));
					}
				} else {
					player.addGroup(makeGroup(result));
				}
			}
			// ADD completions
			completionsStmt = Query.GET_PLAYER_COMPLETIONS.prepare(this);
			completionsStmt.setInt(1, id);
			result = completionsStmt.executeQuery();
			while (result.next()) {
				player.addCompletion(makeCompletion(result));
			}
			
			return player;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}

}
