
package de.ivu.elect.presentation.security;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.inject.Inject;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.business.security.control.ElectPrincipalMarker;
import de.ivu.elect.jee.cdi.BeanManagerUtil;
import de.ivu.elect.jee.faces.Escape;
import de.ivu.elect.jee.faces.FacesContextUtil;
import de.ivu.elect.jee.faces.FacesMessageUtil;
import de.ivu.elect.jee.security.LoginIpBestimmer;
import de.ivu.elect.presentation.security.sso.SingleSignOnView;
import de.ivu.elect.presentation.security.tfa.TfaSupported;

public class LoginPhaseListener implements PhaseListener {

  private static final Logger LOG = LoggerFactory.getLogger(LoginPhaseListener.class);

  private static final long serialVersionUID = 1L;

  public static final AtomicBoolean LOGGE_ZUGRIFF_LOGIN_SEITE = new AtomicBoolean(false);

  @Inject
  private transient ElectPrincipalMarker principal;

  @Inject
  private transient LoginGewuenschteSeite loginGewuenschteSeite;

  @Inject
  private transient MandantenNummerUrlParameter mandantenNummerUrlParameter;

  @Inject
  private transient TfaSupported tfaSupported;

  @Inject
  private transient LoginIpBestimmer loginIpBestimmer;

  @Inject
  private transient Escape escape;

  @Override
  public void afterPhase(PhaseEvent event) {
    if (isUserLoggedIn()) {
      if (isUserHasToChangePassword()) {
        if (isChangePasswordPage(event.getFacesContext())) {
          // alles OK, Passwort muss gewechselt werden
        } else {
          redirectToChangePasswordPage(event.getFacesContext());
        }
      } else if (isUserHasToTfaChallenge()) {
        if (isTfaChallengePage(event.getFacesContext())) {
          // alles OK, TFA Challenge muss bestanden werden
        } else {
          redirectToTfaChallengePage(event.getFacesContext());
        }
      } else {
        stelleRequestParameterWiederHer(event);
      }
    } else {
      if (isLoginPage(event.getFacesContext())) {
        loggeZugriffLoginSeite(event.getFacesContext());
      } else {
        LoginGewuenschteSeite loginGewuenschteSeite = holeLoginGewuenschteSeite();
        if (!isChangePasswordPage(event.getFacesContext()) && !checkSessionAbgelaufen(event.getFacesContext())) {
          Map<String, String> requestParameterMap = event.getFacesContext().getExternalContext().getRequestParameterMap();
          loginGewuenschteSeite.setViewId(event.getFacesContext().getViewRoot().getViewId(), requestParameterMap);
          mandantenNummerUrlParameter.setMandantenNummerAsString(requestParameterMap.get(MandantenNummerUrlParameter.MANDANT_PARAMETER));
        } else {
          loginGewuenschteSeite.setViewId(null, new HashMap<>());
        }
        redirectToLoginPage(event.getFacesContext());
      }
    }
  }

  private void loggeZugriffLoginSeite(FacesContext facesContext) {
    if (LOGGE_ZUGRIFF_LOGIN_SEITE.get()) {
      if (facesContext == null || facesContext.getViewRoot() == null) {
        return;
      }
      LOG.info("Login Seite: " + facesContext.getViewRoot().getViewId() + ", " + loginIpBestimmer.bestimmeIpAdresse(facesContext, false));
    }
  }

  private void redirectToTfaChallengePage(FacesContext fc) {
    FacesContextUtil.redirectToView(fc, tfaSupported.getTfaChallengeView());
  }

  private boolean isTfaChallengePage(FacesContext fc) {
    if (checkSessionAbgelaufen(fc) || StringUtils.isBlank(fc.getViewRoot().getViewId())) {
      return false;
    }
    return tfaSupported.getTfaChallengeViews().contains(fc.getViewRoot().getViewId());
  }

  private boolean isUserHasToTfaChallenge() {
    return tfaSupported.isTfaAvailable() && getPrincipal().isMustTfaChallenge();
  }

  private void stelleRequestParameterWiederHer(PhaseEvent event) {
    if (!FacesContext.getCurrentInstance().isPostback() && !FacesContextUtil.isAjaxRequest()) {
      try {
        BeanManagerUtil.getBeanByClass(ElectRedirectParameterEvaluation.class).accept(event.getFacesContext().getExternalContext().getRequestParameterMap());
      } catch (Exception e) {
        LOG.warn("Fehler beim Herstellen der redirect Parameter - ignoriere Parameter", e);
      }
    }
  }

  private LoginGewuenschteSeite holeLoginGewuenschteSeite() {
    return loginGewuenschteSeite;
  }

  private ElectPrincipalMarker getPrincipal() {
    return principal;
  }

  private void redirectToChangePasswordPage(FacesContext fc) {
    FacesContextUtil.redirectToView(fc, ChangePasswordView.VIEW_ID);
  }

  private boolean isUserHasToChangePassword() {
    return getPrincipal().isMustChangePassword();
  }

  private boolean isChangePasswordPage(FacesContext fc) {
    if (checkSessionAbgelaufen(fc)) {
      return false;
    }
    return ChangePasswordView.VIEW_ID.equals(fc.getViewRoot().getViewId());
  }

  private boolean checkSessionAbgelaufen(FacesContext fc) {
    if (fc == null || fc.getViewRoot() == null) {
      FacesMessageUtil.addInfoMessage("login.session.abgelaufen");
      return true;
    }
    return false;
  }

  private void redirectToLoginPage(FacesContext fc) {
    FacesContextUtil.redirectToView(fc, SingleSignOnView.VIEW_ID);
  }

  private boolean isUserLoggedIn() {
    return getPrincipal().isLoggedIn();
  }

  private boolean isLoginPage(FacesContext fc) {
    if (checkSessionAbgelaufen(fc)) {
      return false;
    }

    return LoginView.VIEW_ID.equals(fc.getViewRoot().getViewId()) || SingleSignOnView.VIEW_ID.equals(fc.getViewRoot().getViewId());
  }

  @Override
  public void beforePhase(PhaseEvent event) {
    // Do nothing!
  }

  @Override
  public PhaseId getPhaseId() {
    return PhaseId.RESTORE_VIEW;
  }
}
