package controller.person;

import business.user.PersonManager;
import javax.enterprise.context.SessionScoped;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import javax.security.enterprise.AuthenticationStatus;
import javax.security.enterprise.SecurityContext;
import javax.security.enterprise.credential.Password;
import javax.security.enterprise.credential.UsernamePasswordCredential;
import javax.servlet.http.HttpSession;

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

import controller.AbstractController;
import exception.InvalidEmailException;
import exception.InvalidPasswordException;
import exception.PersonInaktiveException;
import httpauthenticationmechanism.ManagedPerson;
import java.io.ByteArrayInputStream;
import model.person.Person;

import static javax.security.enterprise.AuthenticationStatus.SEND_FAILURE;
import static javax.security.enterprise.AuthenticationStatus.SUCCESS;

import java.io.Serializable;
import java.util.Set;
import javax.ejb.EJB;
import static javax.security.enterprise.authentication.mechanism.http.AuthenticationParameters.withParams;
import model.files.UserPicture;
import model.person.enums.UserGroup;
import org.omnifaces.cdi.Param;
import static org.omnifaces.util.Faces.getRequest;
import static org.omnifaces.util.Faces.getResponse;
import static org.omnifaces.util.Faces.redirect;
import org.primefaces.model.DefaultStreamedContent;
import org.primefaces.model.StreamedContent;

/**
 *
 * @author Patrick
 */
@Named
@SessionScoped
public class PersonController extends AbstractController implements Serializable {

    private static final long serialVersionUID = -2257766986862616262L;
    final static Logger LOGGER = LogManager.getLogger(PersonController.class);
    private String username;
    private String password;
    private boolean rememberMe = false;
    
    @Inject 
    SecurityContext securityContext;

    @Inject
    ManagedPerson managedPerson;

    @Param(name = "continue") // Defined in @LoginToContinue of SecurityFormAuthenticationMechanism
    private boolean loginToContinue;

    private Person activePerson;
    
    @EJB
    PersonManager personManager;

    public PersonController() {

    }

    public String submit() {

        try {
            // credential that want to be validate was UsernamePasswordCredential
            UsernamePasswordCredential credential = new UsernamePasswordCredential(username, new Password(password));

            // this will call our security configuration to authorize the user
            AuthenticationStatus status = securityContext.authenticate(
                    getRequest(),
                    getResponse(),
                    withParams()
                    .credential(credential)
                    .newAuthentication(!loginToContinue)
                    .rememberMe(rememberMe)
            );

            // When logged in choose the right page by class. When more then one group
            // fits then the higher order is used
            if (status.equals(SUCCESS)) {
                managedPerson.addLogin(username);
                activePerson = personManager.getByEmail(username).get();
                
                FacesContext facesContext = FacesContext.getCurrentInstance();
                HttpSession session = (HttpSession) facesContext.getExternalContext().getSession(true);
                session.setAttribute("user", this);
                session.setAttribute("realUsername", username);
                
                if (securityContext.isCallerInRole(UserGroup.ADMIN.toString())) {
                    LOGGER.info("Login succesfull " + username + " with role: ADMIN");
                    return "admin/welcome.xhtml?faces-redirect=true";
                }

                if (securityContext.isCallerInRole(UserGroup.USER.toString())) {
                    LOGGER.info("Login succesfull " + username + " with role: USER");
                    return "user/welcome.xhtml?faces-redirect=true";
                }
                
                if (securityContext.isCallerInRole(UserGroup.CUSTOMER.toString())) {
                    LOGGER.info("Login succesfull " + username + " with role: USER");
                    return "customer/welcome.xhtml?faces-redirect=true";
                }

                redirect("index.xhtml");

            } else if (status.equals(SEND_FAILURE)) {

                sendErrorMessage("Fehler!", "Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut!.");
                return "";
            }
        } catch (InvalidPasswordException | InvalidEmailException e) {
            LOGGER.info("Wrong Email or Password: " + username);
            sendErrorMessage("Fehler!", "Falsche Email oder Passwort!");
            return "";
        } catch (PersonInaktiveException p){
            sendErrorMessage("Fehler!", " Ihr Konto ist inatkiv. Bitte wenden Sie sich an den Administator.");
            return "";
        } catch (Exception e) {
            LOGGER.error("Login error with " + e);
            sendErrorMessage("Fehler!", "Ein Fehler ist aufgetreten, bitte versuchen Sie es erneut!");
            return "";
        }
        return "";
    }
    
    public StreamedContent getImage() {
        if (activePerson == null) {
            return null;
        }
        UserPicture picture = activePerson.getUserPicture();
        
        return DefaultStreamedContent.builder()
                .contentType(picture == null ? null : picture.getMime().getMimeType())
                .stream(() -> {
                    if (picture == null || picture.getFileData() == null) {
                        return null;
                    }

                    try {
                        return new ByteArrayInputStream(picture.getFileData());
                    }
                    catch (Exception e) {
                        LOGGER.error(e);
                        return null;
                    }
                })
                .build();
    }
    
    public boolean hasPicture(){
        return activePerson.getUserPicture() != null;
    }

    public String logout() {
        LOGGER.info("User is logging out: " + username);
        try {
            FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
            managedPerson.removeLogin(username);
            activePerson = null;
            redirect("index.xhtml");
        } catch (Exception e) {
            LOGGER.error("couldn't logout " + username + "with:" + e);
        }

        return "index.xhtml";
    }
    
    public void saveActivePerson(){
        personManager.refresh(activePerson);
    }
    
    public void reloadActivePerson() {
        this.activePerson = personManager.load(activePerson);
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public boolean isRememberMe() {
        return rememberMe;
    }

    public void setRememberMe(boolean rememberMe) {
        this.rememberMe = rememberMe;
    }
    
    public Person getActiveUser() {
        return activePerson;
    }

    public void setActiveUser(Person activeUser) {
        this.activePerson = activeUser;
    }

    public Set<String> getLogins() {
        return managedPerson.getLogins();
    }
}
