
package de.ivu.elect.business.benutzerverwaltung.entity;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Collectors;

import javax.persistence.Transient;

import de.ivu.elect.business.benutzerverwaltung.boundary.AbstractRolleDAO;
import de.ivu.elect.business.mandant.entity.Mandant;
import de.ivu.elect.business.security.UseCasePermission;
import de.ivu.elect.jee.entity.customdata.EmptyCustomData;
import de.ivu.elect.jee.security.AbstractPrincipal;
import de.ivu.elect.jee.security.Login;
import de.ivu.elect.jee.security.LoginServiceFactory;
import de.ivu.elect.jee.security.PasswordEncryptionService;


import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

public abstract class AbstractBenutzer implements BenutzerBase, Login<BenutzerPK> {

  private static final long serialVersionUID = 1L;

  public static final Long DEFAULT_LOGIN_VERSUCHE = 0L;

  public static final Long ACCOUNT_GESPERRT_AB_LOGIN_VERSUCHE = 6L;

  public static final Benutzer SYSTEM;

  public static final String DEAKTIVIERT = "DEAKTIVIERT";

  static {
    SYSTEM = new Benutzer(EmptyCustomData.INSTANCE);
    SYSTEM.setBenutzerkennung(AbstractPrincipal.SYSTEM_BENUTZERKENNUNG);
    SYSTEM.setVorname("SYSTEM");
    SYSTEM.setFamilienname("SYSTEM");
  }

  @Transient
  private transient String passwortToEncrypt;

  protected AbstractBenutzer() {
    super();
  }

  public void setRollen(Collection<Rolle> rollen) {
    removeAllRolles();

    rollen.forEach(rolle -> {
      if (rolle != null) {
        addRolle(rolle);
      }
    });
  }

  public boolean hasPasswortToEncrypt() {
    return isNotBlank(getPasswortToEncrypt());
  }

  public String getPasswortToEncrypt() {
    return passwortToEncrypt;
  }

  public void setPasswortToEncrypt(String plainPasswort) {
    this.passwortToEncrypt = plainPasswort;
  }

  public boolean isMussPasswortWechseln() {
    return getMussPasswortWechseln();
  }

  public void setMussPasswortWechseln() {
    setMussPasswortWechseln(true);
  }

  @Override
  public void erhoeheFehlversuche() {
    setLoginVersuche(getLoginVersuche() + 1);
    checkIsGesperrt();
  }

  private void checkIsGesperrt() {
    // ELECT-3808 - Admin-User nicht mehr sperren
    if (!isAdmin() && getLoginVersuche() >= ACCOUNT_GESPERRT_AB_LOGIN_VERSUCHE) {
      setGesperrt(true);
    }
  }

  public boolean isAdmin() {
    return LoginServiceFactory.ADMIN_KENNUNG.equalsIgnoreCase(getBenutzerkennung());
  }

  @Override
  public void erfolgreicherLogin() {
    setLoginVersuche(DEFAULT_LOGIN_VERSUCHE);
  }

  @Override
  public void passwortErfolgreichGewechselt() {
    setMussPasswortWechseln(false);
  }

  @Override
  public void updateLastLoginTime() {
    setLetzteAnmeldung(new Date());
  }

  public Collection<UseCasePermission> getPermissions(BenutzerEinsatzort benutzerEinsatzort) {
    Collection<UseCasePermission> result = new ArrayList<>();
    for (Rolle rolle : getAlleRollen(benutzerEinsatzort)) {
      result.addAll(rolle.getRecht());
    }
    return result;
  }

  @Deprecated // nicht verwenden BenutzerEinsatzort fehlt
  public List<Rolle> getRollenSortedByNameAsc() {
    Set<Rolle> rolles = getRolles();
    List<Rolle> result = new ArrayList<>(rolles);

    result.sort(Comparator.comparing(Rolle::getName));
    return result;
  }

  @Override
  @Deprecated // NICHT VERWENDEN, ENTHALTEN DIE ROLLEN DES BENUTZEREINSATZORTES NICHT!
  public List<String> getRollenAsStrings() {
    return getRolles().stream().map(Rolle::getName).sorted().collect(Collectors.toList());
  }

  public List<String> getRollenAsStrings(BenutzerEinsatzort benutzerEinsatzort) {
    return getAlleRollen(benutzerEinsatzort).stream().map(Rolle::getName).sorted().collect(Collectors.toList());
  }

  public Set<Rolle> getAlleRollen(BenutzerEinsatzort benutzerEinsatzort, AbstractRolleDAO rolleDAO) {
    Set<Rolle> result = new HashSet<>(getRolles(rolleDAO));
    if (benutzerEinsatzort != null) {
      result.addAll(benutzerEinsatzort.getRolles(rolleDAO));
    }
    return result;
  }

  private java.util.List<Rolle> getRolles(AbstractRolleDAO rolleDAO) {
    return rolleDAO.findByBenutzer((Benutzer) this);
  }

  @Deprecated
  public Set<Rolle> getAlleRollen(BenutzerEinsatzort benutzerEinsatzort) {
    Set<Rolle> result = new HashSet<>(getRolles());
    if (benutzerEinsatzort != null) {
      result.addAll(benutzerEinsatzort.getRolles());
    }
    return result;
  }

  @Override
  public void setPasswort(PasswordEncryptionService passwordEncryptionService, String neuesUnverschluesseltesPasswort) {
    setSalt(passwordEncryptionService.generateSaltAsBase64EncodedString());
    setPasswort(passwordEncryptionService.getEncryptedPasswordAsBase64EncodedString(neuesUnverschluesseltesPasswort, getSalt()));
  }

  @Override
  public boolean isGesperrt() {
    return Boolean.TRUE.equals(getGesperrt());
  }

  @Override
  public Long getMandantenNummer() {
    Mandant mandant = getMandant();

    if (mandant == null) {
      return null;
    }

    return mandant.getNummer();
  }

  public boolean isTanListeKonfiguriert() {
    return isNotBlank(getTanliste());
  }

  public boolean isHotpKonfiguriert() {
    return isNotBlank(getHotpSecret());
  }

  public boolean isTanListeAktiviert() {
    return isTanListeKonfiguriert() && !DEAKTIVIERT.equals(getTanliste());
  }

  public boolean isHotpAktiviert() {
    return isHotpKonfiguriert() && !DEAKTIVIERT.equals(getHotpSecret());
  }

  public String getBenutzerkennungProtokoll() {
    String result = getBenutzerkennung();

    if (isNotBlank(getProtokollalias())) {
      result = getProtokollalias(); // ELECT-8253
    }

    return formatBenutzerkennungMitMandant(result);
  }

  public String getBenutzerkennungMitMandant() {
    return formatBenutzerkennungMitMandant(getBenutzerkennung());
  }

  private String formatBenutzerkennungMitMandant(String value) {
    String result = value;

    if (!getMandant().getArt().isOhneMandant()) {
      result = result + " [" + getMandant().getNummer() + "]";
    }

    return result;
  }

  protected String preProcessBenutzerkennung(String neuerWert) {
    if (isBlank(neuerWert)) {
      return neuerWert;
    }

    if (AbstractPrincipal.SYSTEM_BENUTZERKENNUNG.equalsIgnoreCase(neuerWert)) {
      return AbstractPrincipal.SYSTEM_BENUTZERKENNUNG;
    }

    return neuerWert.toLowerCase(Locale.getDefault()); // ELECTKOM-252
  }

  protected void preSetBenutzerkennung(String alterWert, String neuerWert) {
    // nichts zu tun
  }

  protected void postSetBenutzerkennung(String alterWert, String neuerWert) {
    // nichts zu tun
  }
}
