/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package business.user;

import java.io.Serializable;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.ejb.Stateless;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.transaction.Transactional;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import model.person.Password;
import model.person.Salt;
import model.person.Person;

/**
 *
 * @author Patrick
 */
@Named(value = "passwordManager")
@Stateless
public class PasswordManager implements Serializable {
	private static final long serialVersionUID = -4563304131856981259L;

	final static Logger LOGGER = LogManager.getLogger(PasswordManager.class);

    @PersistenceContext(name = "pu_person")
    private EntityManager em;

    //private Password password;
    private Person user;

    private final int keyLength = 256;

    public byte[] hashPassword(final char[] password, final byte[] salt, final int iterations) {
        try {
            SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
            PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, this.keyLength);
            SecretKey key = skf.generateSecret(spec);
            //this.password = new Password(this.costumer, key.getEncoded());
            LOGGER.debug("Hash created!");
            return key.getEncoded();
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            LOGGER.error("Failure creating hash for with:" + e);
            return null;
            //throw new RuntimeException( e );
        }
    }

    public byte[] hashPasswordUser(final char[] password, Person user) {

        if (user == null) {
            LOGGER.error("Tried to create hash for Nullcostumer!");
            return null;
        }
        try {
            SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
            PBEKeySpec spec = new PBEKeySpec(password, user.getSalt().getSalt(), user.getSalt().getInterations(), keyLength);
            SecretKey key = skf.generateSecret(spec);
            LOGGER.debug("Hash created!");
            return key.getEncoded();
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            LOGGER.error("Failure creating hash for" + user.getEmail() + "with: " + e);
            return null;
        }
    }
    
    public boolean passwordCheckEmail(final String email, final byte[] password) {
        TypedQuery<Person> query = em.createNamedQuery(Person.FIND_BY_EMAIL, Person.class);
        query.setParameter("email", email);
        try {
            Person user;
            user = query.getSingleResult();

            boolean equals = Arrays.equals(password, user.getPassword().getPassword());

            for (int i = 0; i < password.length; i++) {
                password[i] = 0;
            }

            return equals;
        } catch (NoResultException e) {
            return false;
        }
    }

    @Transactional
    public boolean passwordCheckCustomer(final Person user, final String password) {
        LOGGER.debug("Test " + user.getEmail() + " mit " + password);
        byte[] pw = hashPasswordUser(password.toCharArray(), user);
        
        boolean equals = Arrays.equals(pw, user.getPassword().getPassword());
        
        for (int i = 0; i < pw.length; i++) {
            pw[i] = 0;
        }
        
        return equals;

    }

    public boolean changePassword(Person user, String newPassword, String oldPassword) {
        if (user == null) {
            LOGGER.error("Nullcostumer tried to change Password");
            return false;
        }
        
        if(passwordCheckCustomer(user, oldPassword)){
            user.getPassword().setPassowrd(hashPasswordUser(newPassword.toCharArray(), user));
            LOGGER.info("Password changed for " + user.getEmail());
        } else {
            return false;
        }
        
        try {
            em.persist(user);
            LOGGER.info("Password changed for " + user.getEmail());
            return true;
        } catch (Exception e) {
            LOGGER.error("Couldn't save new password to " + user.getEmail() + " with: " + e.toString());
            return false;
        }
    }

    public byte[] generateRandomPassword() {
        SecureRandom random = new SecureRandom();
        byte bytes[] = new byte[20];
        random.nextBytes(bytes);
        return bytes;
    }
    
    public Password gerateNewRandomPasswordClass(Person user){
        Salt salt = user.getSalt();
        String pass = Arrays.toString(generateRandomPassword());
        return new Password(user, hashPassword(pass.toCharArray(), salt.getSalt(), salt.getInterations()));
    }
    
    public byte[] hashToken(String token){
        try {
            SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
            byte[] test = "SuperTestSalz".getBytes();
            PBEKeySpec spec = new PBEKeySpec(token.toCharArray(), test, 200, this.keyLength);
            SecretKey key = skf.generateSecret(spec);
            LOGGER.debug("TokenHash created!");
            return key.getEncoded();
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            LOGGER.error("Failure creating tokenhash for " + token + " with: " + e);
            return null;
        }
    }

	public Person getUser() {
		return user;
	}

	public void setUser(Person user) {
		this.user = user;
	}
}
