package com.uca.dao;

import com.uca.entity.UserEntity;
import com.uca.entity.UserInfosEntity;
import org.mindrot.jbcrypt.BCrypt;

import java.sql.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.Calendar;


public class UserDAO extends _Generic<UserEntity> {

    //Renvoie tous les utilisateur (non utilisé)
    public ArrayList<UserEntity> getAllUsers() {
        ArrayList<UserEntity> entities = new ArrayList<>();
        try {
            PreparedStatement preparedStatement = this.connect.prepareStatement("SELECT * FROM users ORDER BY login ASC;");
            ResultSet resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
                UserEntity entity = new UserEntity();
                entity.setLogin(resultSet.getInt("login"));
                entity.setPseudo(resultSet.getString("pseudo"));
                entity.setEmail(resultSet.getString("email"));
                entity.setHashpswd(resultSet.getString("hashpswd"));
                java.sql.Date sqlDate = resultSet.getDate("last_date_co");
                if(sqlDate != null)
                {
                    java.util.Date utilDate = new java.util.Date(sqlDate.getTime());
                    entity.setLastCoDate(utilDate);
                }

                entities.add(entity);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return entities;
    }

    //Renvoie un untilisateur avec le login donné
    public UserEntity getUserById(int id)
    {
        UserEntity entity = new UserEntity();
        try {
            PreparedStatement preparedStatement = this.connect.prepareStatement("SELECT * FROM users WHERE login = (?);");
            preparedStatement.setString(1, String.valueOf(id));
            ResultSet resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
                entity.setLogin(resultSet.getInt("login"));
                entity.setPseudo(resultSet.getString("pseudo"));
                entity.setEmail(resultSet.getString("email"));
                entity.setHashpswd(resultSet.getString("hashpswd"));
                java.sql.Date sqlDate = resultSet.getDate("last_date_co");
                if(sqlDate != null)
                {
                    java.util.Date utilDate = new java.util.Date(sqlDate.getTime());
                    entity.setLastCoDate(utilDate);
                }

            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return entity;
    }

    //Renvoie un l'utilisateur avec le mail donné
    public Integer getUserIdByMail(String mail)
    {
        if(!userEmailExist(mail))
        {
            return null;
        }
        try {
            PreparedStatement preparedStatement = this.connect.prepareStatement("SELECT login FROM users WHERE email = (?);");
            preparedStatement.setString(1, mail);
            ResultSet resultSet = preparedStatement.executeQuery();
            if(resultSet.next())
            {
                return resultSet.getInt("login");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    //Vérifie si le mot de passe associé au mail est valide
    private Boolean pswdCheck(String pswd, String mail)
    {
        int id  = getUserIdByMail(mail);
        Boolean exist = null;
        try {
            //Récupere le salt et le hash
            PreparedStatement preparedStatement = this.connect.prepareStatement("SELECT salt,hashpswd FROM users WHERE login = (?) ;");
            preparedStatement.setInt(1, id);
            ResultSet resultSet = preparedStatement.executeQuery();
            if(resultSet.next())
            {
                //Utilise le salt pour hasher le mdp donné et compare au mdp de la BDD
                String hashedPassword = BCrypt.hashpw(pswd, resultSet.getString("salt"));
                exist =  hashedPassword.equals(resultSet.getString("hashpswd"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return exist;
    }

    //Détecte si c'est la première connexion journaliere du l'utilisateur
    private Boolean firstCo(int id)
    {
        Boolean result = null;
        try {
            PreparedStatement preparedStatement = this.connect.prepareStatement("SELECT * FROM users WHERE login = (?);");
            preparedStatement.setString(1, String.valueOf(id));
            ResultSet resultSet = preparedStatement.executeQuery();
            if(resultSet.next())
            {
                java.sql.Date sqlDate = resultSet.getDate("last_date_co");
                java.util.Date lastCoDate = sqlDate == null ? new java.util.Date(0) : new java.util.Date(sqlDate.getTime());

                Calendar lastCoCal = Calendar.getInstance();
                lastCoCal.setTime(lastCoDate);

                Calendar actualDateCal = Calendar.getInstance();

                if (lastCoCal.get(Calendar.DAY_OF_YEAR) != actualDateCal.get(Calendar.DAY_OF_YEAR)) {
                    result = true;
                } else {
                    result = false;
                }
                preparedStatement = this.connect.prepareStatement("UPDATE USERS SET LAST_DATE_CO = (?) WHERE LOGIN = (?);");
                java.util.Date date = new java.util.Date();

                preparedStatement.setDate(1, new java.sql.Date(date.getTime()));
                preparedStatement.setInt(2, id);

                preparedStatement.executeUpdate();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return result;
    }

    //Vérifie si le mail est déja enregistré dans la BDD
    public Boolean userEmailExist(String mail)
    {

        Boolean exist = null;
        try {

            PreparedStatement preparedStatement = this.connect.prepareStatement("SELECT * FROM users WHERE email = ? ;");
            preparedStatement.setString(1, mail);
            ResultSet resultSet = preparedStatement.executeQuery();
            exist =  resultSet.next();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return exist;
    }

    //Calcule et renvoie des infos pour le profil de l'utilisateur
    public UserInfosEntity getUserInfo(int id)
    {
        UserInfosEntity userInfo = new UserInfosEntity();
        try {
            PreparedStatement preparedStatement = this.connect.prepareStatement("SELECT count(*) as count FROM own WHERE idowner = ? ;");
            preparedStatement.setInt(1, id);
            ResultSet resultSet = preparedStatement.executeQuery();
            if(resultSet.next())
                userInfo.setPkm(resultSet.getInt("count"));
            preparedStatement = this.connect.prepareStatement("SELECT count(distinct idpkm) as count FROM own WHERE idowner = ? ;");
            preparedStatement.setInt(1, id);
            resultSet = preparedStatement.executeQuery();
            if(resultSet.next())
                userInfo.setDistinctPkm(resultSet.getInt("count"));
            preparedStatement = this.connect.prepareStatement("SELECT count(*) as count FROM own WHERE idowner = ? AND shiny = true;");
            preparedStatement.setInt(1, id);
            resultSet = preparedStatement.executeQuery();
            if(resultSet.next())
                userInfo.setShiny(resultSet.getInt("count"));
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return userInfo;
    }

    //Vérifie 
    public int login(String pswd, String email)
    {
        if(!userEmailExist(email))       
        {        
            return 3;
        }
        int result = 0;
        Boolean bool = pswdCheck(pswd, email);
        if(bool)
            result = 1;
        else if(!bool)
            result = 3;
        else
            result = 0;
        if(result == 1)
        {
            if(firstCo(getUserIdByMail(email)))
            {
                return 2;
            }
            else 
                return 1;
            
        }
        return result;
    }

    //Vérifie s'il reste des lvlup a l'utilisateur
    public Boolean lvlup(int id)
    {
        Boolean possible = null;
        try {
            PreparedStatement preparedStatement = this.connect.prepareStatement("SELECT lvlup FROM users WHERE login = (?) ;");
            preparedStatement.setInt(1, id);
            ResultSet resultSet = preparedStatement.executeQuery();
            if(resultSet.next())
            {
                possible = resultSet.getInt("lvlup") > 0 ? true : false;
                if(possible)
                {
                    preparedStatement = this.connect.prepareStatement("UPDATE USERS SET lvlup = (?) WHERE LOGIN = (?);");

                    preparedStatement.setInt(1, resultSet.getInt("lvlup") - 1);
                    preparedStatement.setInt(2, id);

                    preparedStatement.executeUpdate();
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return possible;
    }

    //Renvoie tous les utlisateur ayant pour login ou psedo la String donné
    public ArrayList<UserEntity> userSearch(String search) 
    {
        ArrayList<UserEntity> entities = new ArrayList<>();
        try {
            PreparedStatement preparedStatement = this.connect.prepareStatement("SELECT * FROM users WHERE pseudo = (?) OR login = (?) ORDER BY login ASC;");
            preparedStatement.setString(1,search);
            preparedStatement.setInt(2,Integer.parseInt(search));
            ResultSet resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
                UserEntity entity = new UserEntity();
                entity.setLogin(resultSet.getInt("login"));
                entity.setPseudo(resultSet.getString("pseudo"));
                entity.setEmail(resultSet.getString("email"));
                entity.setHashpswd(resultSet.getString("hashpswd"));
                java.sql.Date sqlDate = resultSet.getDate("last_date_co");
                if(sqlDate != null)
                {
                    java.util.Date utilDate = new java.util.Date(sqlDate.getTime());
                    entity.setLastCoDate(utilDate);
                }

                entities.add(entity);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return entities;
    }

    //Vérifie si un utilisateur existe
    public Boolean userExist(int id)
    {
        Boolean exist = null;
        try {

            PreparedStatement preparedStatement = this.connect.prepareStatement("SELECT * FROM users WHERE login = ? ;");
            preparedStatement.setInt(1, id);
            ResultSet resultSet = preparedStatement.executeQuery();
            exist =  resultSet.next();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return exist;
    }

    //Met a jour les lvlup de tout les utilisateur a 5
    public void updateLvlUp()
    {
        try { 
            PreparedStatement preparedStatement = this.connect.prepareStatement("UPDATE USERS SET lvlup = 5;");
            preparedStatement.executeUpdate();

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //Crée un utilisateur
    @Override
    public UserEntity create(UserEntity user) {
        try {
            PreparedStatement preparedStatement = this.connect.prepareStatement("INSERT INTO users(pseudo, email, salt, hashpswd, last_date_co, lvlup) VALUES(?,?,?,?,?,?);");
            preparedStatement.setString(1, user.getPseudo());
            preparedStatement.setString(2, user.getEmail());
            String salt = BCrypt.gensalt();
            preparedStatement.setString(3, salt);
            String hashedPassword = BCrypt.hashpw(user.getHashpswd(), salt);
            user.setHashpswd(hashedPassword);
            preparedStatement.setString(4, hashedPassword);
            preparedStatement.setDate(5, null);
            preparedStatement.setInt(6, 5);
            preparedStatement.executeUpdate();
            return user;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    //Supprime un utilisateur, non implémenté
    @Override
    public void delete(UserEntity obj) {
            //TODO !
    }
}
