package controllers.eSecurePlay1;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import models.eSecurePlay1.EsAccount;
import net.eclettica.esecure.dao.EsDao;
import net.eclettica.esecure.play.web.AuthWebConfig;
import net.eclettica.esecure.services.AuthService;
import net.eclettica.esecure.services.EsExceptionLogin;
import net.eclettica.esecure.services.EsExceptionSession;
import play.Logger;
import play.data.validation.Validation;
import play.mvc.Before;
import play.mvc.Controller;
// import play.mvc.Scope.Session;
import play.mvc.Util;

/**
 * Per usare questo controller utilizzare:
 * 
 * <pre>
 * <code>
 * 
 * &#64;With(Auth.class) public abstract SecureController extends BaseController { }
 * 
 * &#64;Check({'operator-manager', 'manutenzione'}) 
 * public class Operator extends SecureController { 
 *     ...
 * 
 * </code>
 * </pre>
 * 
 * oppure:
 * 
 * <pre>
 * <code>
 * 
 * &#64;Check({'superadmin', 'manutenzione'}) 
 * public class Admin extends SecureController {
 * 
 *     &#64;Check('manutenzione') 
 *     public void deleteAccount() { ... }
 * 
 * 
 * </code>
 * </pre>
 * 
 * Al momento del login usare il metodo diretto: Auth.authenticate(login,
 * password) che salva in sessione l'id di sessione
 * 
 * Per recuperare l'id di sessione usare: Auth.getUserSessionId();
 * 
 * I percorsi utilizzati di default sono:
 * 
 * <ul>
 * <li>/login -> pagina di login
 * <li>/logout -> pagina di logout
 * <li>/noAccess -> pagina di accesso negato
 * </ul>
 * 
 * @author sviluppo
 * @author gannunziata
 * 
 */
public class Auth extends Controller {

	public static AuthWebConfig cfg = new AuthWebConfig();
	private static AuthService authService = AuthService.instance();

	// NEW BRANCH

	public static enum Play_Session_Keys {
		CURR_ES_ACCOUNT_ID("curr.esaccount.uuid"), CURR_AUTH_TOKEN("curr.user.authToken"), CURR_PARSED_JSON(
				"curr.parsed.json");

		private Play_Session_Keys(String value) {
			this.value = value;
		}

		private String value;

		public String getValue() {
			return value;
		}

		public void setValue(String value) {
			this.value = value;
		}
	}

	/**
	 * @param email
	 * @return
	 */
	@Util
	public static String createAccount(String email, String password, String group) {
		EsAccount esA = new EsAccount();
		esA.setUsername(email);
		EsDao eSecure = new EsDao();
		esA.setPassword(eSecure.cryptPassword(password));
		esA.setUserGroup(group);
		esA.save();

		return esA.uuid;
	}

	@Util
	public static boolean authenticate(String username, String password) {
		String sessionId = null;
		Validation.required("user.username", username);
		Validation.required("user.password", password);
		if (Validation.hasErrors())
			return false;

		try {
			sessionId = authService.login(username, password);
			if (sessionId == null) {
				Logger.info("errore di login per utente %s", username);
				return false;
			}
			Auth.setCurrentEsAccountId(sessionId);
			Auth.setCurrentAuthToken(sessionId);
			return true;

		} catch (EsExceptionLogin e) {
			Logger.warn(e, "login error with %s", username);
			return false;
		}

	}

	@Util
	public static void setCurrentAuthToken(String sessionId) {
		renderArgs.put(Play_Session_Keys.CURR_AUTH_TOKEN.getValue(), sessionId);
	}

	@Util
	public static void setCurrentEsAccountId(String sessionId) {
		renderArgs.put(Play_Session_Keys.CURR_ES_ACCOUNT_ID.getValue(), getCurrentEsAccountId(sessionId));
	}

	@Util
	public static boolean isLogged(String sessionId) {
		return AuthService.instance().current(sessionId) != null;
	}

	@Util
	public static String getCurrentEsAccountId(String sessionId) {
		return AuthService.instance().current(sessionId);
	}

	@Util
	public static String getCurrentEsAccountId() {
		return (String) renderArgs.get(Play_Session_Keys.CURR_ES_ACCOUNT_ID.getValue());
	}

	@Util
	public static String getCurrentUserSessionId() {
		return (String) renderArgs.get(Play_Session_Keys.CURR_AUTH_TOKEN.getValue());
	}

	@Util
	public static void logout(String sessionId) {
		AuthService.instance().logout(sessionId);
	}

	@Retention(RetentionPolicy.RUNTIME)
	@Target({ ElementType.METHOD, ElementType.TYPE })
	public @interface Check {
		String[] value();
	}

	@Retention(RetentionPolicy.RUNTIME)
	@Target({ ElementType.METHOD, ElementType.TYPE })
	public @interface NoCheck {

	}

	@Before
	public static boolean checkAccess() {

		// public NoCheck annotation
		NoCheck pub = getActionAnnotation(NoCheck.class);
		if (pub != null) {
			return true;
		}

		// Checks
		Check check = getActionAnnotation(Check.class);
		if (check != null) {
			return check(check);
		} else {
			check = getControllerAnnotation(Check.class);
			if (check != null) {
				return check(check);
			}
		}

		return true;

	}

	@Util
	private static boolean check(Check check) {
		String sessionId = getCurrentUserSessionId();

		for (String profile : check.value()) {
			try {
				if (!AuthService.instance().canIDo(sessionId, profile)) {
					return false;
				}
			} catch (EsExceptionSession ees) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Can I do something with authority auth.
	 * 
	 * @param auth
	 *            to check
	 * @return true if you can, false otherwise
	 */
	@Util
	public static boolean canIDo(String auth) {
		String sessionId = Auth.getCurrentUserSessionId();
		try {
			if (auth.equals("logged")) {
				boolean res = Auth.isLogged(sessionId);
				return res;
			}

			return AuthService.instance().canIDo(sessionId, auth);
		} catch (Exception e) {
			return false;
		}
	}

	// END NEW BRANCH

	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	//

	/**
	 * 
	 * @param username
	 * @param password
	 * @return
	 */
	public static String verify(String username, String password) {
		try {
			return AuthService.instance().verify(username, password);
		} catch (EsExceptionLogin e) {
			Logger.warn(e, "verify problem (%s)", e.getLocalizedMessage());
		}
		return null;
	}

	public static boolean autologin(String username) {
		String uuidMe = null;
		try {

			uuidMe = AuthService.instance().autologin(username);

			if (uuidMe == null) {
				Logger.info("errore di login per utente %s", username);
				return false;
			}

			return true;

		} catch (EsExceptionLogin e) {
			Logger.error(e, "login error with %s", username);
			return false;
		}
	}

	/**
	 * TODO move to service!!!
	 * 
	 * @param username
	 * @param newpassword
	 */
	@Util
	public static void saveNewPassword(String username, String newpassword) {
		EsDao dao = new EsDao();
		try {
			String uuid = dao.findByLogin(username);
			if (uuid == null)
				return;

			dao.changePassword(uuid, newpassword);
		} catch (Exception e) {
			Logger.error("problem to save new password: %s", e.getLocalizedMessage());
		}

	}
}
