/*
 * JBoss, a division of Red Hat
 * Copyright 2013, Red Hat Middleware, LLC, and individual
 * contributors as indicated by the @authors tag. See the
 * copyright.txt in the distribution for a full listing of
 * individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.gatein.security.oauth.spi;

import java.io.IOException;
import java.util.concurrent.ExecutionException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.exoplatform.services.organization.UserProfile;
import org.gatein.security.oauth.exception.OAuthException;

/**
 * Processor to call operations on given OAuth provider (Social network)
 *
 * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
 */
public interface OAuthProviderProcessor<T extends AccessTokenContext> {


    /**
     * Process OAuth workflow for this OAuth provider (social network). Workflow is finished if returned {@link org.gatein.security.oauth.spi.InteractionState}
     * is in state {@link org.gatein.security.oauth.spi.InteractionState.State#FINISH} and in this case, InteractionState should also have accessToken filled.
     *
     * If {@link org.gatein.security.oauth.spi.InteractionState} is in state {@link org.gatein.security.oauth.spi.InteractionState.State#AUTH}, then more redirections are needed. In this case,
     * given {@link HttpServletResponse} should be already committed and prepared for redirection.
     *
     * @param httpRequest
     * @param httpResponse
     * @return InteractionState with state of OAuth interaction
     * @throws IOException if IO error occured (for example if httpResponse.sendRedirect failed)
     * @throws OAuthException in case of some other error, which may be specific for this OAuth processor (Details are available in error code)
     * Caller should be able to handle at least {@link org.gatein.security.oauth.exception.OAuthExceptionCode#USER_DENIED_SCOPE}
     * which happens when user denied scope (authorization screen in web of given social network)
     */
    InteractionState<T> processOAuthInteraction(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws
            IOException, OAuthException, ExecutionException, InterruptedException;


    /**
     * Possibility to create new OAuth interaction with custom scope (not just the scope which is provided in configuration of this OAuth processor)
     * @see #processOAuthInteraction(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
     *
     * @param httpRequest
     * @param httpResponse
     * @param scope custom scope, which contains all scopes in single String divided by {@link AccessTokenContext#DELIMITER}
     * @return
     * @throws IOException
     * @throws OAuthException
     */
    InteractionState<T> processOAuthInteraction(HttpServletRequest httpRequest, HttpServletResponse httpResponse, String scope) throws
            IOException, OAuthException, ExecutionException, InterruptedException;


    /**
     * Revoke given access token on OAuth provider side, so application is removed from list of supported applications for given user
     *
     * @param accessToken access token to revoke
     * @throws OAuthException with code {@link org.gatein.security.oauth.exception.OAuthExceptionCode#TOKEN_REVOCATION_FAILED}
     * if remote revocation of access token failed for some reason
     */
    void revokeToken(T accessToken) throws OAuthException;


    /**
     * Send request to OAuth Provider to validate if given access token is valid and ask for scopes, which are available for given accessToken.
     * Returned access token should be always valid and prepared for invoke other operations
     *
     * @param accessToken accessToken which will be used to ask OAuthProvider about validation and for available scopes
     * @return accessTokenContext, which will be quite same as the one from accessToken parameter. It could have some info updated (like scopes)
     * @throws OAuthException usually with codes:
     *  - {@link org.gatein.security.oauth.exception.OAuthExceptionCode#ACCESS_TOKEN_ERROR} if invalid access is used as argument
     *  - {@link org.gatein.security.oauth.exception.OAuthExceptionCode#IO_ERROR} if IO error occurs
     */
    T validateTokenAndUpdateScopes(T accessToken) throws OAuthException;

    /**
     * Return object, which can be used to call some operations on this Social network. For example "Plus" object for Google+ network
     *
     * @param socialApiObjectType Type of object, which we wanted to return. Method will return null if this type is not supported by this processor
     * @param accessToken access token used to initialize object
     * @return initialized object of required type or null if type wasn't found (supported) by this processor
     */
    <C> C getAuthorizedSocialApiObject(T accessToken, Class<C> socialApiObjectType);

    // OPERATIONS FOR ACCESS TOKEN PERSISTENCE


    /**
     * Save accessToken data to given userProfile. Note that we are not calling any DB save operations, just filling data into
     * given userProfile
     *
     * @param userProfile where data about access token will be filled
     * @param codec to encode some attributes (sensitive data) before save them to user profile
     * @param accessToken specific access token for this OAuth processor
     */
    void saveAccessTokenAttributesToUserProfile(UserProfile userProfile, OAuthCodec codec, T accessToken);


    /**
     * Obtain needed data from given userProfile and create accessToken from them
     *
     * @param userProfile where data from access token will be obtained
     * @param codec to decode data from userProfile
     * @return accesstoken or null if accessToken is not found in persistent storage
     */
    T getAccessTokenFromUserProfile(UserProfile userProfile, OAuthCodec codec);


    /**
     * Remove data about access token from this user profile
     * @param userProfile from which data will be removed
     */
    void removeAccessTokenFromUserProfile(UserProfile userProfile);
}
