import java.util.Date;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.sakaiproject.tool.api.Session;
import org.sakaiproject.tool.cover.SessionManager;

import org.sakaiproject.id.cover.IdManager;

import org.sakaiproject.component.cover.ServerConfigurationService;


import org.sakaiproject.user.api.User;
import org.sakaiproject.user.cover.UserDirectoryService;
import org.sakaiproject.site.cover.SiteService;

import org.apache.axis.AxisFault;
import org.apache.axis.Constants;
import org.apache.axis.MessageContext;

// Necessary until code moves into UsageSessionServiceAdaptor.java
import org.sakaiproject.event.api.UsageSession;
import org.sakaiproject.event.cover.UsageSessionService;
import org.sakaiproject.authz.cover.AuthzGroupService;
import org.sakaiproject.event.cover.EventTrackingService;

/**
 * class to support Sakai Login headers sent in request as a string array
 */
public class SakaiPortalLogin {

    private static final Log LOG = LogFactory.getLog(SakaiPortalLogin.class);

    private User getSakaiUser(String id, String pw)
    {
        User user = null ;

	try {
        	user = UserDirectoryService.getUserByEid(id);
	} catch (Exception e) {
		user = null;
	}
	return user;
    }

    public String loginAndCreate(String id, String pw, String firstName, String lastName, String eMail)
        throws AxisFault
    {

	MessageContext messageContext = MessageContext.getCurrentContext(); 
	String ipAddress = messageContext.getStrProp(Constants.MC_REMOTE_ADDR);

    	String portalSecret = ServerConfigurationService.getString("webservice.portalsecret");
	String portalIPs = ServerConfigurationService.getString("webservice.portalIP");
	String ipCheck = ServerConfigurationService.getString("webservice.IPCheck");

	// Leave this in for a while until this is better tested

	System.out.println("SakaiPortalLogin.loginAndCreate id="+id+" pw="+pw+" sec="+portalSecret);
	System.out.println("SakaiPortalLogin.loginAndCreate ip="+ipAddress+" portalIP="+portalIPs+" IPCheck="+ipCheck);
	System.out.println("        fn="+firstName+" ln="+lastName+" em="+eMail+" ip="+ipAddress);

	if ( portalSecret == null || pw == null || 
	     portalSecret.equals("") || ! portalSecret.equals(pw) ) {
		LOG.info("SakaiPortalLogin secret mismatch ip="+ipAddress);
                throw new AxisFault("Failed login");
	}

        // Verify that this IP address matches our string
	if ( "true".equalsIgnoreCase(ipCheck) )
	{
		if (  portalIPs == null || portalIPs.equals("") ||  portalIPs.indexOf(ipAddress) == -1 ) {
			System.out.println(" SakaiPortalLogin Trusted IP not found" );
		 	LOG.info("SakaiPortalLogin Trusted IP not found");
                 	throw new AxisFault("Failed login");
		}
	}

        User user = getSakaiUser(id,pw);

	if ( user == null && firstName != null && lastName != null && eMail != null ) {
		System.out.println("Creating Sakai Account...");
		try {
			// Set password to something unguessable - they can set a new PW once they are logged in
			String hiddenPW = IdManager.createUuid();
 			UserDirectoryService.addUser(null,id,firstName,lastName,eMail,hiddenPW,"registered", null);
                        System.out.println("User Created...");
		} catch(Exception e) {
			System.out.println("Unable to create user...");
        		throw new AxisFault("Failed login");
		}
        	user = getSakaiUser(id,pw);
	}

	if ( user != null ) {
		System.out.println("Have User");
                Session s = SessionManager.startSession();
                SessionManager.setCurrentSession(s);
                if (s == null)
                {
                        // System.out.println("no session established");
                        LOG.warn("Web Services Login failed to establish session for id="+id+" ip="+ipAddress);
                        throw new AxisFault("Unable to establish session");
                }
                else
                {
			// We do not care too much on the off-change that this fails - folks simply won't show up in presense
			// and events won't be trackable back to people / IP Addresses - but if it fails - there is nothing
			// we can do anyways.

			UsageSessionService_loginDirect(user.getId(), id, ipAddress, "SakaiPortalLogin.jws");

			try {
				String siteId = SiteService.getUserSiteId(s.getUserId());
				System.out.println("Site exists..."+siteId);
			} catch(Exception e) {
				System.out.println("Site does not exist...");
        			throw new AxisFault("Failed login");
			}
                	if ( LOG.isDebugEnabled() ) LOG.debug("Sakai Portal Login id="+id+" ip="+ipAddress+" session="+s.getId());
			return s.getId();
                }
	}
	LOG.info("SakaiPortalLogin Failed ip="+ipAddress);
        throw new AxisFault("Failed login");
    }

    public String login(String id,String pw) 
        throws AxisFault
    {
	System.out.println("SakiaPortalLogin.login()");
	return loginAndCreate(id, pw, null, null, null);
    }

    // This code is adapted from the file:
    // ./event-impl/impl/src/java/org/sakaiproject/event/impl/UsageSessionServiceAdaptor.java
    // Method
    // public boolean login(Authentication authn, HttpServletRequest req)
    // We want to do exactly what this routine *does* but we do not have an HttpServletRequest
    // to hand it

        public boolean UsageSessionService_loginDirect(String userId, String userEid, String ipAddress, String userAgent)
        {
                // establish the user's session - this has been known to fail
                UsageSession session = UsageSessionService.startSession(userId,ipAddress,userAgent);
                if (session == null)
                {
                        return false;
                }

                // set the user information into the current session
                Session sakaiSession = SessionManager.getCurrentSession();
                sakaiSession.setUserId(userId);
                sakaiSession.setUserEid(userEid);

                // update the user's externally provided realm definitions
                AuthzGroupService.refreshUser(userId);

		String EVENT_LOGIN = "user.login";

                // post the login event
                EventTrackingService.post(EventTrackingService.newEvent(EVENT_LOGIN, null, true));

                return true;
        }
}
