/*
 * 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.time.Instant;
import java.util.Optional;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.transaction.Transactional;

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

import controller.person.PersonController;
import exception.InvalidEmailException;
import exception.InvalidPasswordException;
import exception.PersonInaktiveException;
import java.util.HashSet;
import java.util.Set;
import model.person.Salt;
import model.person.enums.TokenType;
import model.person.Person;
import model.person.enums.Call;
import model.person.enums.UserGroup;

/**
 *
 * @author Patrick
 */
@Named
@Stateless
public class PersonManager implements Serializable {

    private static final long serialVersionUID = -6581582446436303658L;

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

    @EJB
    private PasswordManager passwordManager;

    @Inject
    private PersonController userController;

    @PersistenceContext(name = "pu_person", type = PersistenceContextType.EXTENDED)
    private EntityManager em;

    @Transactional
    public void demo() {
        Salt salt = new Salt();
        Salt salt2 = new Salt();
        Salt salt3 = new Salt();

        Set<UserGroup> groupUser = new HashSet<>();
        groupUser.add(UserGroup.USER);
        
        Set<UserGroup> adminUser = new HashSet<>();
        adminUser.add(UserGroup.ADMIN);

        Person test = new Person(
                "user@test.de",
                (passwordManager.hashPassword("test".toCharArray(), salt.getSalt(), salt.getInterations())),
                salt,
                groupUser
        );

        test.setFirstname("User");
        test.setLastname("Nachname");
        test.setMobile("0124584589");
        test.setFax("3445565675");
        test.setTelefon("042154585");
        test.setActive(true);
        test.setCall(Call.HERR);
        em.persist(test);
        
        Person testAdmin = new Person(
                "admin@test.de",
                (passwordManager.hashPassword("admin".toCharArray(), salt2.getSalt(), salt2.getInterations())),
                salt2,
                adminUser
        );

        testAdmin.setFirstname("Admin");
        testAdmin.setLastname("Administratori");
        testAdmin.setMobile("0124584589");
        testAdmin.setFax("3445565675");
        testAdmin.setTelefon("042154585");
        testAdmin.setActive(true);
        testAdmin.setCall(Call.HERR);
        em.persist(testAdmin);
        
        Person testInactive = new Person(
                "inaktive@test.de",
                (passwordManager.hashPassword("test".toCharArray(), salt3.getSalt(), salt3.getInterations())),
                salt3,
                groupUser
        );

        testInactive.setFirstname("Admin");
        testInactive.setLastname("Administratori");
        testInactive.setMobile("0124584589");
        testInactive.setFax("3445565675");
        testInactive.setTelefon("042154585");
        testInactive.setActive(false);
        testInactive.setCall(Call.HERR);
        em.persist(testInactive);
    }

    @Transactional
    public Person load(Person user) {
        try {
            Person loaded = this.em.find(Person.class, user.getId());
            LOGGER.info(loaded);

            return loaded;
        } catch (Exception e) {
            LOGGER.error("Error", e);
            return null;
        }
    }

    @Transactional
    public Person getActiveUser() {
        try {
            Person user = em.find(Person.class, userController.getActiveUser().getId());
            return user;
        } catch (Exception e) {
            LOGGER.error("couldn't load user: " + e);
            return null;
        }
    }

    @Transactional
    public Person getPlainActiveUser() {
        try {
            Person user = em.find(Person.class, userController.getActiveUser().getId());
            return user;
        } catch (Exception e) {
            LOGGER.error("couldn't load user: " + e);
            return null;
        }
    }

    public Optional<Person> getByEmail(String email) {
        try {
            Person person = this.em.createNamedQuery(Person.FIND_BY_EMAIL, Person.class)
                    .setParameter("email", email).getSingleResult();
            
            if (person != null) {
                person.getUserGroups().size();
            }
            
            return Optional.of(person);
        } catch (Exception e) {
            return Optional.empty();
        }
    }

    public Optional<Person> getByLoginToken(String loginToken, TokenType tokenType) {
        Optional<Person> optional;
        try {

            optional = Optional.of(this.em.createNamedQuery(Person.FIND_BY_TOKEN, Person.class)
                    .setParameter("tokenHash", this.passwordManager.hashToken(loginToken))
                    .setParameter("tokenType", tokenType)
                    .setParameter("timestamp", Instant.now())
                    .getSingleResult()
            );
            return optional;
        } catch (Exception e) {
            return Optional.empty();
        }
    }

    public Person getByEmailAndPassword(String email, String password) {
        Person managedUser = getByEmail(email).orElseThrow(InvalidEmailException::new);
        LOGGER.debug("Loaded " + managedUser.getEmail());
        if (!passwordManager.passwordCheckCustomer(managedUser, password)) {
            throw new InvalidPasswordException();
        }
        if (!managedUser.isActive()) {
            throw new PersonInaktiveException();
        }
        userController.setActiveUser(managedUser);
        return managedUser;
    }

    @Transactional
    public boolean save(Person user) {
        if (user == null) {
            LOGGER.error("Tried to save null or Nullcustomer");
            return false;
        }
        try {
            if (user.getId() != null && user.getId() > 0) {
                em.merge(user);
            } else {
                em.persist(user);
            }
            LOGGER.info("Saved " + user.getEmail());
            return true;
        } catch (Exception e) {
            LOGGER.error("Tried to save " + user.getEmail() + " with error: " + e);
            return false;
        }
    }

    public void refresh(Person user) {
        if (user == null) {
            LOGGER.error("Tried to save null or Nullcustomer");
            return;
        }

        try {
            em.refresh(user);
        } catch (Exception e) {
            LOGGER.error("Tried to refresh " + user.getEmail() + " with error: " + e);
        }
    }

    public String resetPassword() {
        //TODO
        return null;
    }
}
