package com.atlassian.stash.internal.auth;

import java.util.Optional;

import com.atlassian.bitbucket.auth.*;
import com.atlassian.bitbucket.user.ApplicationUser;
import com.atlassian.bitbucket.user.NoSuchUserException;
import com.atlassian.stash.internal.user.StashUserAuthenticationToken;

import javax.annotation.Nonnull;

/**
 * Services that handles user authentication. All authentication methods delegate authentication to the configured
 * (and enabled) {@link HttpAuthenticationHandler authentication handlers}, in order of their
 * {@link HttpAuthenticationHandlerModuleDescriptor#getWeight() weight}.
 *
 * @since 4.6
 */
public interface AuthenticationService {

    /**
     * Attempts to authenticate the specified user given the details on the
     * {@link javax.servlet.http.HttpServletRequest request}. Different authentication handlers will be interested in
     * different parts of the requests ({@code Authorization} header, cookies, custom headers, ..).
     * <p>
     * Note: this method only authenticates; it does not set the Spring Security
     * {@link org.springframework.security.core.context.SecurityContext}, nor does it call any of the authentication
     * {@link HttpAuthenticationSuccessHandler success} or {@link HttpAuthenticationFailureHandler failure} handlers.
     *
     * @param context the authentication context
     * @return the Spring Security authentication token representing the authenticated user, or {@link Optional#empty()}
     *         if no user was authenticated.
     */
    @Nonnull
    Optional<StashUserAuthenticationToken> authenticate(@Nonnull HttpAuthenticationContext context);

    /**
     * Attempts to authenticate the specified user given their password. Failed authentications will not count towards
     * the maximum number allowed before CAPTCHA is imposed for the supplied user.
     * <p>
     * Note: this method only authenticates; it does not set the Spring Security
     * {@link org.springframework.security.core.context.SecurityContext}.
     *
     * @param username the username
     * @param password the user's password
     * @return the {@link ApplicationUser} representing the authenticated user
     * @throws AuthenticationSystemException if a failure occurs
     * @throws ExpiredPasswordAuthenticationException if the user's password has expired and must be changed
     * @throws InactiveUserAuthenticationException if the specified user is inactive
     * @throws IncorrectPasswordAuthenticationException if the provided password is incorrect
     * @throws NoSuchUserException if the specified user does not exist or their user details cannot be retrieved
     */
    @Nonnull
    ApplicationUser authenticate(@Nonnull String username, @Nonnull String password);

    /**
     * Attempts to authenticate the specified user given their password. Failed authentications will count towards
     * the maximum number allowed before CAPTCHA is imposed for the supplied user. If the maximum number of allowed
     * failed logins has already been exceeded the method will throw {@link CaptchaRequiredAuthenticationException}
     *
     * @param username the username
     * @param password the user's password
     * @return the {@link ApplicationUser} representing the authenticated user
     * @throws AuthenticationSystemException if a failure occurs in one of the authentication handlers
     * @throws CaptchaRequiredAuthenticationException if CAPTCHA is required
     * @throws ExpiredPasswordAuthenticationException if the user's password has expired and must be changed
     * @throws InactiveUserAuthenticationException if the specified user is inactive
     * @throws IncorrectPasswordAuthenticationException if the provided password is incorrect
     * @throws NoSuchUserException if the specified user does not exist or their user details cannot be retrieved
     */
    @Nonnull
    ApplicationUser authenticateWithCaptcha(@Nonnull String username, @Nonnull String password);
}
