package com.atlassian.stash.internal.auth;

import com.atlassian.bitbucket.auth.HttpAuthenticationContext;
import com.atlassian.bitbucket.auth.HttpAuthenticationHandler;
import com.atlassian.bitbucket.auth.HttpAuthenticationHandlerModuleDescriptor;
import com.atlassian.bitbucket.user.ApplicationUser;
import com.atlassian.bitbucket.util.ModuleDescriptorUtils;
import com.atlassian.bitbucket.util.Timer;
import com.atlassian.bitbucket.util.TimerUtils;
import com.atlassian.plugin.PluginAccessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Nonnull;

import static java.util.stream.Collectors.toList;

@Component("pluginHttpAuthenticationHandler")
public class PluginHttpAuthenticationHandler implements HttpAuthenticationHandler {

    private static final Logger log = LoggerFactory.getLogger(PluginHttpAuthenticationHandler.class);

    private final PluginAccessor pluginAccessor;

    @Autowired
    public PluginHttpAuthenticationHandler(PluginAccessor pluginAccessor) {
        this.pluginAccessor = pluginAccessor;
    }

    @Override
    public ApplicationUser authenticate(@Nonnull HttpAuthenticationContext context) {
        for (HttpAuthenticationHandler handler : getHandlers()) {
            String handlerName = handler.getClass().getName();
            try (Timer ignored = TimerUtils.start(handlerName + ".authentication")) {
                log.trace("authenticate - trying handler {}", handlerName);
                ApplicationUser user = handler.authenticate(context);
                if (user != null) {
                    log.trace("authenticate - handler {} authenticated user {}", handlerName, user.getName());
                    return user;
                }
            }
        }

        log.trace("authenticate - none of the plugins handled authentication");
        return null;
    }

    @Override
    public void validateAuthentication(@Nonnull HttpAuthenticationContext context) {
        for (HttpAuthenticationHandler handler : getHandlers()) {
            String handlerName = handler.getClass().getName();
            try (Timer ignored = TimerUtils.start(handlerName + ".validateAuthentication")) {
                log.trace("validateAuthentication - Calling handler {}.validateAuthentication", handlerName);
                handler.validateAuthentication(context);
            } catch (RuntimeException e) {
                log.debug("{} {} failed validation", HttpAuthenticationHandler.class.getSimpleName(), handlerName, e);
                throw e;
            }
        }
    }

    private Iterable<HttpAuthenticationHandler> getHandlers() {
        return ModuleDescriptorUtils.toSortedModules(
                pluginAccessor.getEnabledModuleDescriptorsByClass(HttpAuthenticationHandlerModuleDescriptor.class))
                .collect(toList());
    }
}
