package edu.utah.nanofab.coralapiserver.auth;


import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Optional;
import io.dropwizard.auth.AuthenticationException;
import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.basic.BasicCredentials;

import edu.utah.nanofab.coralapi.CoralAPI;
import edu.utah.nanofab.coralapiserver.TokenConfiguration;


public class SimpleAuthenticator implements Authenticator<BasicCredentials, User> {
    private TokenConfiguration[] tokens;
  private ConcurrentHashMap<String, TokenConfiguration> sessionTokens;
  private String coralIor;
  private String coralConfigUrl;
  public static final Logger logger = LoggerFactory.getLogger(SimpleAuthenticator.class);   

  public SimpleAuthenticator(TokenConfiguration[] tokens, 
      ConcurrentHashMap<String, TokenConfiguration> sessionTokens, 
      String coralIor, 
      String coralConfigUrl) {
    super();
    this.tokens = tokens;
    this.sessionTokens = sessionTokens;
    this.coralIor = coralIor;
    this.coralConfigUrl = coralConfigUrl;
  }

  @Override
    public Optional<User> authenticate(BasicCredentials credentials) throws AuthenticationException {
    if (credentials.getUsername().equals("auth-token")) {
                    Optional<User> tokensUser = authenticateByToken(credentials.getPassword());
                    
                    //special case for proxyAuthenticator
                    if (tokensUser.get().getUsername().equals("proxyAuthenticator")) {
                      logger.debug("bypassing coral check for proxyAuthenticator");
                      return tokensUser;
                    } else if (isValidUser(tokensUser)) {
                        return tokensUser;
                    } else {
                      logger.debug("User did not validate against coral: " + tokensUser.get().getUsername());
                        throw new AuthenticationException("Invalid user");
                    }
    } else {
      return authenticateByUsernamePassword(credentials.getUsername(), credentials.getPassword());
    }
    }
    
    public boolean isValidUser(Optional<User> user) {
        if (!user.isPresent())
            return false;
        
        String username = user.get().getUsername();
        CoralAPI api = new CoralAPI(username, this.coralIor, this.coralConfigUrl);
        try {
            api.getMember(username);
        } catch (Exception ex) {
            return false;
        } finally {
            api.close();
        }
        
        return true;
    }
  
  public Optional<User> authenticateByUsernamePassword(String user, String pass) {
    logger.debug("Authenticating " + user + " by password.");
    CoralAPI api = new CoralAPI(user, this.coralIor, this.coralConfigUrl);
    try {
      boolean success = api.authenticate(user, pass);
      api.close();
      if (success) {
        return Optional.of(new User(user));
      }
    } catch (Exception e) {
      api.close();
      e.printStackTrace();
    } finally {
      api.close();
    }
    return Optional.<User>absent();
  }
  
  public Optional<User> authenticateByToken(String requestToken) {
    logger.debug("Authenticating by token: " + requestToken);
    
    Date now = new Date();
    for (TokenConfiguration t : tokens) {
      if ( t.getToken().equals(requestToken)) {
        if (t.getExpiration().getTime() > now.getTime() ) {
          logger.debug("Token valid for " + t.getUser() + " until " + t.getExpiration());
          return Optional.of(new User(t.getUser()));
        } else {
          logger.debug("Token expired: " + t.getUser() + " on " + t.getExpiration());
        }
      }
    }
    logger.debug("Token not found in config file");
    
    return authenticateBySessionToken(requestToken);
  }

  private Optional<User> authenticateBySessionToken(String requestToken) {
    logger.debug("Checking session tokens");    
    Date now = new Date();
    if (sessionTokens.containsKey(requestToken)){
      TokenConfiguration token = sessionTokens.get(requestToken);
      if (token.getExpiration().getTime() > now.getTime()) {
        logger.debug("Token valid: " + token.getUser() + " until " + token.getExpiration());
        return Optional.of(new User(sessionTokens.get(requestToken).getUser()));
      } else {
        logger.debug("Token expired: " + token.getUser() + " on " + token.getExpiration());
      }
    }
    logger.debug("Token not found in session tokens");    
    return Optional.<User>absent();
  }
}

@@ -1259,16 +1259,17 @@
 ption %7B%0A
+%09
     if (
@@ -1314,24 +1314,25 @@
 -token%22)) %7B%0A
+%09
             
@@ -1411,24 +1411,25 @@
 assword());%0A
+%09
             
@@ -1429,32 +1429,33 @@
                %0A
+%09
                 
@@ -1496,16 +1496,17 @@
 ticator%0A
+%09
         
@@ -1517,24 +1517,247 @@
         if (
+!tokensUser.isPresent()) %7B%0A%09%09                      logger.debug(%22User did not validate against coral: %22 + credentials.getPassword());%0A%09%09                      return Optional.%3CUser%3Eabsent();%0A%09                    %7D else if ( 
 tokensUser.g
@@ -1803,24 +1803,25 @@
 icator%22)) %7B%0A
+%09
             
@@ -1888,24 +1888,25 @@
 nticator%22);%0A
+%09
             
@@ -1926,32 +1926,33 @@
 urn tokensUser;%0A
+%09
                 
@@ -1989,24 +1989,25 @@
 ensUser)) %7B%0A
+%09
             
@@ -2037,16 +2037,17 @@
 nsUser;%0A
+%09
         
@@ -2059,32 +2059,33 @@
        %7D else %7B%0A
+%09
                 
@@ -2174,26 +2174,25 @@
 sername());%0A
-  
+%09
             
@@ -2205,59 +2205,41 @@
     
-throw new AuthenticationException(%22Invalid user%22
+return Optional.%3CUser%3Eabsent(
 );%0A
+%09
     
@@ -2252,24 +2252,25 @@
           %7D%0A
+%09
     %7D else %7B
@@ -2266,24 +2266,25 @@
    %7D else %7B%0A
+%09
       return
@@ -2366,24 +2366,25 @@
 assword());%0A
+%09
     %7D%0A    %7D%0A

fix error handling for bad auth