package io.core9.module.auth;

import io.core9.module.auth.session.SessionConnector;
import io.core9.module.auth.wrappers.ServerRequestToken;
import io.core9.module.auth.wrappers.SubjectWrapper;
import io.core9.plugin.server.Cookie;
import io.core9.plugin.server.Server;
import io.core9.plugin.server.handler.Middleware;
import io.core9.plugin.server.request.Request;

import java.util.Map;

import net.xeoh.plugins.base.annotations.PluginImplementation;
import net.xeoh.plugins.base.annotations.events.PluginLoaded;
import net.xeoh.plugins.base.annotations.injections.InjectPlugin;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SessionsSecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionException;
import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.subject.Subject;

@PluginImplementation
public class AuthenticationPluginImpl implements AuthenticationPlugin {
	
	private static final String REQ_SUBJECT_KEY = "auth.subject";
	
	@InjectPlugin
	private Server server;
	
	private SessionConnector sessionsConnector;
	private DefaultSecurityManager manager;
	
	@PluginLoaded
	public void onAuthenticationConnectorAvailable(AuthenticationConnector connector) {
		manager = connector.getSecurityManager();
	}
	
	@PluginLoaded
	public void onSessionConnectorAvailable(SessionConnector sessionConnector) {
		this.sessionsConnector = sessionConnector;
	}

	@Override
	public User getUser(Request req) {
        return new SubjectWrapper(getSubject(req));
	}

	@Override
	public void execute() {
		if(manager != null) {
			SecurityUtils.setSecurityManager(manager);
		} else {
			manager = (DefaultSecurityManager) SecurityUtils.getSecurityManager();
		}
		if(sessionsConnector != null) {
			DefaultSessionManager sessionManager = (DefaultSessionManager) manager.getSessionManager();
			sessionManager.setSessionDAO(sessionsConnector.getSessionDAO());
			sessionManager.setSessionFactory(sessionsConnector.getSessionFactory());
		}
		server.use("/system/login", new Middleware() {
			@Override
			public void handle(Request request) {
				switch(request.getMethod()) {
				case POST:
					Map<String,Object> map = request.getBodyAsMap().toBlocking().last();
					Subject subject = getSubject(request);
					String username = (String) map.get("username");
					String password = (String) map.get("password");
					try {
						subject.login(new ServerRequestToken(request, username, password));
						String path = (String) map.get("path");
						if(path != null) {
							request.getResponse().sendRedirect(301, path);
						} else {
							request.getResponse().end();
						}
					} catch (AuthenticationException e) {
						request.getResponse().setStatusCode(401);
						request.getResponse().addValue("content", "Login failed, please supply an existing username/password combination");
					}
					break;
				default:
					request.getResponse().end("Only POST requests allowed.");
					break;
				}
			}
		});
	}
	
	/**
	 * Return the subject from a request
	 * @param req
	 * @return
	 */
	private Subject getSubject(Request req) {
		Subject currentUser = req.getContext(REQ_SUBJECT_KEY);
        if (currentUser == null) {
        	Cookie cookie = req.getCookie("CORE9SESSIONID");
        	Subject.Builder builder = new Subject.Builder();
        	if(cookie != null) {
        		// Try to build an existing session
        		SessionsSecurityManager o = (SessionsSecurityManager) SecurityUtils.getSecurityManager();
        		Session session = null;
        		try {
        			session = ((DefaultSessionManager) o.getSessionManager()).getSessionDAO().readSession(cookie.getValue());
        			builder = builder.sessionId(session.getId());
        		} catch (SessionException e) {
        			// Unset existing cookie, as the session doesn't exist
        			cookie.setMaxAge(-10);
        			req.getResponse().addCookie(cookie);
        			cookie = null;
        		}
        	}
        	currentUser = builder.buildSubject();
        	if(cookie == null) {
        		cookie = server.newCookie("CORE9SESSIONID", currentUser.getSession(true).getId().toString());
        		cookie.setPath("/");
        		req.getResponse().addCookie(cookie);
        	}
        	req.putContext(REQ_SUBJECT_KEY, currentUser);
        }
        currentUser.getSession().touch();
        return currentUser;
	}
}

@@ -715,32 +715,77 @@
 ecurityManager;%0A
+import org.apache.shiro.mgt.SecurityManager;%0A
 import org.apach
@@ -1220,125 +1220,31 @@
 %0A%09%0A%09
-private SessionConnector sessionsConnector;%0A%09private DefaultSecurityManager manager;%0A%09%0A%09@PluginLoaded%0A%09public void on
+@InjectPlugin%0A%09private 
 Auth
@@ -1266,19 +1266,10 @@
 ctor
-Available(A
+ a
 uthe
@@ -1290,211 +1290,71 @@
 ctor
- connector) %7B%0A%09%09manager = connector.getSecurityManager();%0A%09%7D%0A%09%0A%09@PluginLoaded%0A%09public void onSessionConnectorAvailable(SessionConnector sessionConnector) %7B%0A%09%09this.sessionsConnector = sessionConnector;%0A%09%7D
+;%0A%09%0A%09@InjectPlugin%0A%09private SessionConnector sessionsConnector;
 %0A%0A%09@
@@ -1490,16 +1490,97 @@
 ute() %7B%0A
+%09%09DefaultSecurityManager manager = authenticationConnector.getSecurityManager();%0A
 %09%09if(man
@@ -1631,23 +1631,60 @@
 Manager(
-m
+authenticationConnector.getSecurityM
 anager
+()
 );%0A%09%09%7D e

use InjectPlugin instead of PluginLoaded