/**
 * This file is part of the Meeds project (https://meeds.io/).
 *
 * Copyright (C) 2020 - 2025 Meeds Association contact@meeds.io
 *
 * This program 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 3 of the License, or (at your option) any later version.
 * This program 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 program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package io.meeds.oauth.spi;

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

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import org.exoplatform.services.organization.UserProfile;

import io.meeds.oauth.exception.OAuthException;

/**
 * Processor to call operations on given OAuth provider (Social network)
 *
 */
public interface OAuthProviderProcessor<T extends AccessTokenContext> {

  /**
   * Process OAuth workflow for this OAuth provider (social network). Workflow
   * is finished if returned
   * {@link io.meeds.oauth.spi.InteractionState} is in state
   * {@link io.meeds.oauth.spi.InteractionState.State#FINISH} and in
   * this case, InteractionState should also have accessToken filled. If
   * {@link io.meeds.oauth.spi.InteractionState} is in state
   * {@link io.meeds.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 io.meeds.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(jakarta.servlet.http.HttpServletRequest,
   *                        jakarta.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 io.meeds.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 io.meeds.oauth.exception.OAuthExceptionCode#ACCESS_TOKEN_ERROR}
   *                          if invalid access is used as argument -
   *                          {@link io.meeds.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);

  /**
   * Logout user from this OAuth provider. Default implementation does nothing.
   * Concrete OAuth processors may override this method to perform specific
   * logout operations on OAuth provider side (for example revoke tokens, call
   * logout endpoints, etc.)
   */
  default boolean processLogout(HttpServletRequest request, HttpServletResponse response, OAuthProviderType<T> oAuthProviderType) throws IOException {
    // Default implementation does nothing
    return false;
  }
}
