/*
 *  Copyright 2013-2050 Emmanuel BRUN (contact@amapj.fr)
 * 
 *  This file is part of AmapJ.
 *  
 *  AmapJ is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.

 *  AmapJ is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with AmapJ.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * 
 */
 package fr.amapj.service.services.utilisateur;

import java.util.List;

import javax.persistence.Query;

import fr.amapj.common.AmapjRuntimeException;
import fr.amapj.common.CollectionUtils;
import fr.amapj.common.DateUtils;
import fr.amapj.common.LongUtils;
import fr.amapj.model.engine.rdblink.RdbLink;
import fr.amapj.model.engine.transaction.DbRead;
import fr.amapj.model.engine.transaction.DbWrite;
import fr.amapj.model.models.acces.RoleList;
import fr.amapj.model.models.fichierbase.EtatUtilisateur;
import fr.amapj.model.models.fichierbase.Utilisateur;
import fr.amapj.service.engine.tools.DbToDto;
import fr.amapj.service.services.access.AccessManagementService;
import fr.amapj.service.services.authentification.ProcessSetPasswordService;
import fr.amapj.service.services.gestioncotisation.GestionCotisationService;
import fr.amapj.service.services.gestioncotisation.PeriodeCotisationUtilisateurDTO;
import fr.amapj.service.services.notification.DeleteNotificationService;
import fr.amapj.service.services.permanence.periode.PeriodePermanenceService;
import fr.amapj.view.engine.popup.formpopup.validator.EmailValidator;
import fr.amapj.view.engine.popup.suppressionpopup.UnableToSuppressException;

/**
 * Permet la gestion des utilisateurs
 */
public class UtilisateurService
{
	
	
	// PARTIE REQUETAGE POUR AVOIR LA LISTE DES UTILISATEURS
	
	/**
	 * Permet de charger la liste de tous les utilisateurs
	 * dans une transaction en lecture
	 * 
	 * si etat = null, alors on charge tous les utilisateurs, quelque soit leur etat
	 */
	@DbRead
	public List<UtilisateurDTO> getAllUtilisateurs(EtatUtilisateur etat)
	{
		RdbLink em = RdbLink.get();
		List<Utilisateur> us = getUtilisateurs(etat);
		return DbToDto.transform(us, (Utilisateur u) ->createUtilisateurDto(em, u));
	}

	
	public UtilisateurDTO createUtilisateurDto(RdbLink em, Utilisateur u)
	{
		UtilisateurDTO dto = new UtilisateurDTO();
		
		dto.id = u.getId();
		dto.nom = u.nom;
		dto.prenom = u.prenom;
		dto.roles = new AccessManagementService().getRoleAsString(em,u);
		dto.email = u.email;
		dto.etatUtilisateur = u.etatUtilisateur;
		dto.dateCreation = u.dateCreation;
		dto.dateModification = u.dateModification;
		dto.lastConnexion = u.lastConnexion;
		
		dto.numTel1 = u.numTel1;
		dto.numTel2 = u.numTel2;
		dto.libAdr1 = u.libAdr1;
		dto.codePostal = u.codePostal;
		dto.ville = u.ville;
		
		dto.processSetPasswordDate = u.processSetPasswordDate;
		dto.processSetPasswordValidityDelay = u.processSetPasswordValidityDelay;
		
		dto.visibleEcranListeAdherent = u.visibleEcranListeAdherent;
		
		return dto;
	}
	
	@DbRead
	public UtilisateurDTO loadUtilisateurDto(Long idUtilisateur)
	{
		RdbLink em = RdbLink.get();
		Utilisateur u = em.find(Utilisateur.class, idUtilisateur);
		return createUtilisateurDto(em, u);
	}


	// PARTIE MISE A JOUR DES UTILISATEURS

	
	@DbWrite
	public void updateUtilisateur(UtilisateurDTO dto)
	{
		RdbLink em = RdbLink.get();
		
		Utilisateur u = em.find(Utilisateur.class, dto.id);
		u.nom = dto.nom;
		u.prenom = dto.prenom;
		u.email = dto.email;
		u.dateModification = DateUtils.getDate();
		
		u.numTel1 = dto.numTel1;
		u.numTel2 = dto.numTel2;
		u.libAdr1 = dto.libAdr1;
		u.codePostal = dto.codePostal;
		u.ville = dto.ville;
		
	}

	
	// PARTIE MISE A JOUR DE L ETAT D'UN UTILISATEUR

	
	@DbWrite
	public void updateEtat(EtatUtilisateur newValue, Long id)
	{
		RdbLink em = RdbLink.get();
		
		Utilisateur u = em.find(Utilisateur.class, id);
		u.etatUtilisateur = newValue;
		u.dateModification = DateUtils.getDate();
	}
	
	
	
	
	// PARTIE CREATION D UN UTILISATEUR

	/**
	 * Permet la création d'un utilisateur  
	 * 
	 * Si mailContent est null, alors aucun mail n'est envoyé (cas de la création des utilisateurs en masse) 
	 */
	@DbWrite
	public UtilisateurInfo createNewUser(UtilisateurDTO utilisateurDTO,String mailTitle,String mailContent)
	{
		RdbLink em = RdbLink.get();
		
		//
		String nom = utilisateurDTO.nom.trim();
		String prenom = utilisateurDTO.prenom.trim();
		String email = utilisateurDTO.email.trim().toLowerCase();
		
		// Vérification de base 
		if (nom.length()==0)
		{
			throw new AmapjRuntimeException("Le nom ne peut pas être vide");
		}
		if (prenom.length()==0)
		{
			throw new AmapjRuntimeException("Le prénom ne peut pas être vide");
		}
		if (EmailValidator.isValidEmail(email)==false)
		{
			throw new AmapjRuntimeException("L'adresse e mail n'est pas valide.");
		}		
		
		// 
		Utilisateur u = new Utilisateur();
		u.nom = nom;
		u.prenom = prenom;
		u.email = email;
		u.dateCreation = DateUtils.getDate();
		u.numTel1 = utilisateurDTO.numTel1;
		u.numTel2 = utilisateurDTO.numTel2;
		u.libAdr1 = utilisateurDTO.libAdr1;
		u.codePostal = utilisateurDTO.codePostal;
		u.ville = utilisateurDTO.ville;
		u.visibleEcranListeAdherent = utilisateurDTO.visibleEcranListeAdherent;
		
		em.persist(u);
		
		UtilisateurInfo res = new UtilisateurInfo();
		res.id = u.id;
		
		if (mailContent!=null)
		{
			new ProcessSetPasswordService().sendMailBienvenue(u.id,mailTitle,mailContent);
		}
		
		return res;
	}

	static public class UtilisateurInfo
	{
		public Long id;
	}
	
	
	// PARTIE SUPPRESSION

	/**
	 * Permet de supprimer un utilisateur 
	 * Ceci est fait dans une transaction en ecriture
	 */
	@DbWrite
	public void deleteUtilisateur(Long id)
	{
		RdbLink em = RdbLink.get();
		
		Utilisateur u = em.find(Utilisateur.class, id);

		int r = countContrat(u,em);
		if (r>0)
		{
			throw new UnableToSuppressException("Cet utilisateur posséde "+r+" contrats.");
		}
		
		List<PeriodeCotisationUtilisateurDTO> ps = new GestionCotisationService().getPeriodeCotisation(id);
		if (ps.size()>0)
		{
			throw new UnableToSuppressException("Cet utilisateur est indiqué comme cotisant sur les périodes de cotisation suivantes :"+CollectionUtils.asStdString(ps, e->e.periodeNom));
		}
		
		List<RoleList> rs = new AccessManagementService().getUserRole(u, em);
		checkRole(rs,RoleList.ADMIN);
		checkRole(rs,RoleList.TRESORIER);
		checkRole(rs,RoleList.PRODUCTEUR);
		checkRole(rs,RoleList.REFERENT);
		
		new DeleteNotificationService().deleteAllNotificationDoneUtilisateur(em, u);
		
		new PeriodePermanenceService().deleteUtilisateur(em,u);
		
		em.remove(u);
	}


	private void checkRole(List<RoleList> rls, RoleList rl)
	{
		if (rls.contains(rl))
		{
			throw new UnableToSuppressException("Cet utilisateur est "+rl+". Enlevez lui ce rôle avant de le supprimer.");
		}
		
	}


	private int countContrat(Utilisateur u, RdbLink em)
	{
		Query q = em.createQuery("select count(c) from Contrat c WHERE c.utilisateur=:u");
		q.setParameter("u", u);
			
		return LongUtils.toInt(q.getSingleResult());
	}
	
	// 
	@DbRead
	public int countContrat(Long idUtilisateur)
	{
		RdbLink em = RdbLink.get();
		Utilisateur u = em.find(Utilisateur.class, idUtilisateur);
		return countContrat(u,em);
	}
	
	// PARTIE REQUETAGE POUR AVOIR LA LISTE DES UTILISATEURS POUR LE SEARCHER
	
	/**
	 * Permet de charger la liste de tous les utilisateurs
	 * dans une transaction en lecture
	 * 
	 * si etat = null, alors on charge tous les utilisateurs, quelque soit leur etat
	 */
	@DbRead
	public List<Utilisateur> getUtilisateurs(EtatUtilisateur etat)
	{
		RdbLink em = RdbLink.get();
		
		if (etat==null)
		{
			em.createQuery("select u from Utilisateur u order by u.nom,u.prenom");
		}
		else
		{
			em.createQuery("select u from Utilisateur u where u.etatUtilisateur=:etat order by u.nom,u.prenom");
			em.setParameter("etat", etat);
		}
		return em.result().list(Utilisateur.class);
	}

	
	// INSERTION EN MASSE DES UTILISATEURS

	@DbWrite
	public void insertAllUtilisateurs(List<UtilisateurDTO> utilisateurs)
	{
		for (UtilisateurDTO utilisateurDTO : utilisateurs)
		{
			createNewUser(utilisateurDTO,null,null);
		}
	}
	
	/**
	 *
	 */
	@DbRead
	public String prettyString(Long idUtilisateur)
	{
		RdbLink em = RdbLink.get();
		
		if (idUtilisateur==null)
		{
			return "";
		}
		
		Utilisateur p = em.find(Utilisateur.class, idUtilisateur);
		return p.nom+" "+p.prenom;
	}
}
