package com.armundia.app.login;

import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.PersistenceContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.Transactional;
import javax.ws.rs.core.Response.Status;

import com.armundia.core.users.Hashing;
import com.armundia.core.users.entity.SeUsers;
import com.armundia.generic.i18n.I18N;
import com.armundia.v2.shared.exception.ArmundiaException;
import com.armundia.v2.shared.interfaces.ISessionUser;

@RequestScoped
public class LoginManager {
	private static final Logger log = Logger.getLogger(LoginManager.class.getName());
	
	private static final String LOGIN_FAILED = "Username o password errati.";
	private static final String NOT_UNIQUE = "Username duplicata.";
	private static final String DISABLED_USER = "Utente disabilitato.";
	private static final String DELETED_USER = "Utente cancellato.";
	
	
	@PersistenceContext
	private EntityManager em;
	
	@Inject
	private ISessionUser sessionUser;
	
	@Inject
	private LoginBroker loginBroker;
	
	@Transactional
	public SeUsers login(String userid, String password, String ipAddress, String userAgent, String clientVersion) throws ArmundiaException {
		if (userid == null || userid.equals("") || password == null) {
			throw this.armundiaExceptionI18N(Status.UNAUTHORIZED, "LOGIN.WRONG_USR_OR_PSW", LOGIN_FAILED);
		}

		log.info(String.format("login: [userId] %s [ipAddress] %s [userAgent] %s [clientVersion] %s", userid, ipAddress, userAgent, clientVersion));

		SeUsers user = null;
		try {
			user = this.getUser(userid);
			
			this.checkPassword(user, password);
		} catch (ArmundiaException ae) {
			loginBroker.insertUserAccess(ae.getMessage(), false, userid, ipAddress, userAgent, clientVersion);
			throw ae;
		}
		
		user.setDataPrecedenteLogin(user.getDataUltimaLogin());
		user.setDataUltimaLogin(new Date());

		loginBroker.insertUserAccess(null, true, userid, ipAddress, userAgent, clientVersion);
		loginBroker.updateAndPersistUser(user);

		return user;
	}
	
	@Transactional
	public DatiUtentePojo postLogin(SeUsers utente, String jsessionId) {
		String userId = utente.getUserid();

		List<String> authorizations = loginBroker.getAuthorizations(userId);
		log.info(String.format("postLogin:  %s trovate %,d autorizzazioni", userId, authorizations.size()));
		
		List<String> roles = loginBroker.getRoles(userId);
		log.info(String.format("postLogin:  %s trovate %,d ruoli", userId, roles.size()));

		DatiUtentePojo datiUtentePojo = new DatiUtentePojo(utente);
		datiUtentePojo.setAuthorizations(authorizations);
		datiUtentePojo.setRoles(roles);
		datiUtentePojo.setUserId(userId);
		datiUtentePojo.setAuthorizations(authorizations);
		datiUtentePojo.setRoles(roles);
		datiUtentePojo.setCodLang(utente.getCodLang());
		datiUtentePojo.setJsessionId(jsessionId);
		
		DatiUtentePojo sessionDup = (DatiUtentePojo) sessionUser;
		sessionDup.setValuesFrom(datiUtentePojo);

		log.info("postLogin: [SessionUser] " + sessionUser.toString());
		log.info(String.format("postLogin: utente [%s] %,d ruoli", userId, sessionUser.getRoles().size()));
		log.info(String.format("postLogin: utente [%s] %,d autorizzazioni", userId, sessionUser.getAuthorizations().size()));

		return datiUtentePojo;
	}
	
	public void logout(HttpServletRequest request, HttpServletResponse response) {

		if (sessionUser == null) {
			log.info("logout [userId] NULL");
		} else {
			log.info("logout [userId] " + sessionUser.getUserId());
		}

		invalidateSession(request, response);
	}
	
	private void invalidateSession(HttpServletRequest request, HttpServletResponse response) {

		if (request.getSession(false) != null) {
			log.info("invalidateSession [sessionId] " + request.getSession(false).getId());
			request.getSession(false).invalidate();
		}

		Cookie[] cookies = request.getCookies();
		if (cookies != null && cookies.length > 0) {
			for (Cookie cookie : cookies) {
				log.fine("Cookie [name] " + cookie.getName() + " [value] " + cookie.getValue());
				cookie.setValue("-");
				cookie.setMaxAge(0);
				response.addCookie(cookie);
			}
		}
	}
	
	private SeUsers getUser(String userid) throws ArmundiaException {
		SeUsers user;
		try {
			user = loginBroker.getUser(userid);
		} catch (NoResultException enr) {
			throw this.armundiaExceptionI18N(Status.UNAUTHORIZED, "LOGIN.WRONG_USR_OR_PSW", LOGIN_FAILED);
		} catch (NonUniqueResultException enu) {
			throw this.armundiaExceptionI18N(Status.CONFLICT, "LOGIN.NOT_UNIQUE_USER", NOT_UNIQUE);
		}

		if (user.getDataDisabled() != null) {
			throw this.armundiaExceptionI18N(Status.UNAUTHORIZED, "LOGIN.DISABLED_USER", DISABLED_USER);
		}
		
		if (user.getDataDeleted() != null) {
			throw this.armundiaExceptionI18N(Status.UNAUTHORIZED, "LOGIN.DELETED_USER", DELETED_USER);
		}
		return user;
	}
	
	private void checkPassword(SeUsers user, String password) throws ArmundiaException {
		boolean checkPassword = false;
		if (user.getPassword() == null || user.getPassword().equals("")) {
			checkPassword = true;
		}
		
		if (!checkPassword) {
			password = password.trim();
			checkPassword = user.getPassword().equals(Hashing.encodeSHA256(password));
		}

		if (!checkPassword) {
			throw this.armundiaExceptionI18N(Status.UNAUTHORIZED, "LOGIN.WRONG_USR_OR_PSW", LOGIN_FAILED);
		}
	}
	
	private ArmundiaException armundiaExceptionI18N(Status status, String code, String message) {
		String mlMessage = I18N.i18n(code, message);
		
		return new ArmundiaException(status, mlMessage);
	}
}
