package com.airlock.iam.authentication.infrastructure.resource;

import com.airlock.iam.authentication.infrastructure.generated.transfer.request.OAuth2AuthorizationUriDataResourceDocument;
import com.airlock.iam.authentication.infrastructure.transfer.request.AuthenticationOAuth2AuthorizationResponseQueryRequest;
import com.airlock.iam.authentication.infrastructure.transfer.request.OAuth2AuthorizationUriData;
import com.airlock.iam.common.domain.model.QueryString;
import com.airlock.iam.flow.api.domain.model.flow.FlowResult;
import com.airlock.iam.flow.api.infrastructure.mapper.FlowResultMapper;
import com.airlock.iam.flow.auth.api.infrastructure.generated.transfer.response.AuthenticationFlowResultDataResourceDocument;
import com.airlock.iam.oauth2.domain.model.sso.OAuth2Authentication;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Response;

@Path("/oauth2/client")
public class OAuth2ClientAuthenticationFlowResource {
   private final FlowResultMapper mapper;
   private final Provider<OAuth2Authentication> oAuth2Authentication;

   @Inject
   public OAuth2ClientAuthenticationFlowResource(FlowResultMapper mapper, Provider<OAuth2Authentication> oAuth2Authentication) {
      this.mapper = mapper;
      this.oAuth2Authentication = oAuth2Authentication;
   }

   @ApiResponse(
      responseCode = "200",
      description = "Authorization Request URI successfully retrieved.",
      content = {
            @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = OAuth2AuthorizationUriDataResourceDocument.class)),
            @Content(mediaType = "application/json", schema = @Schema(implementation = OAuth2AuthorizationUriDataResourceDocument.class))
      }
   )
   @Operation(
      description = "Returns a fresh authorization request URI to initiate the OAuth 2.0 / OpenID Connect handshake on the authorization\nserver. This is needed in situations where pushed authorization requests (see\n<a href=\"https://tools.ietf.org/html/rfc9126\" target=\"_blank\">RFC 9126</a>) or similar standards are used, which\ndo not allow the authorization request URI to be re-used.\n<div class=\"iam-resource-return\">A fresh request authorization URI.</div>",
      tags = "OAuth 2.0/OIDC",
      summary = "Get auth URI"
   )
   @Path("authorization/uri/retrieve")
   @POST
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response retrieveAuthorizationRequestUri() {
      FlowResult result = ((OAuth2Authentication)this.oAuth2Authentication.get()).buildAuthorizationRequest();
      return this.mapper.toResponseWithData(result, OAuth2AuthorizationUriData.class);
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "User successfully identified.",
               content = {
                     @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = AuthenticationFlowResultDataResourceDocument.class)),
                     @Content(mediaType = "application/json", schema = @Schema(implementation = AuthenticationFlowResultDataResourceDocument.class))
               }
            ),
            @ApiResponse(
               responseCode = "403",
               description = "<div class=\"iam-error-code\"><table>\n<tr>\n<td>OAUTH2_CLIENT_AUTHORIZATION_FAILED</td>\n<td>OAuth 2.0 authorization failed. No retries allowed.</td>\n</tr>\n</table>\n</div>\n<br>The current authentication flow has been aborted.\n<br>Possible next step: none.\n",
               content = {
                     @Content(mediaType = "application/vnd.api+json", schema = @Schema(ref = "json-api.yaml#/components/schemas/ErrorDocumentResponse")),
                     @Content(mediaType = "application/json", schema = @Schema(ref = "json-api.yaml#/components/schemas/ErrorDocumentResponse"))
               }
            )
      }
   )
   @Operation(
      description = "Checks the OAuth 2.0 authorization response to authenticate the user. The OAuth 2.0 authorization response\n(see <a href=\"https://tools.ietf.org/html/rfc6749#section-4.1.2\" target=\"_blank\">RFC 6749, Chapter 4.1.2</a>\nrespectively\n<a href=\"https://tools.ietf.org/html/rfc6749#section-4.1.2.1\" target=\"_blank\">RFC 6749, Chapter 4.1.2.1</a> for\nauthorization response errors) has to be submitted in the body as query string from the received OAuth 2.0\nauthorization response request.\n<div class=\"iam-resource-return\">Whether authentication was successful. If the attribute <tt>nextAuthStep</tt> is present in the response,\n further steps are required to successfully authenticate. If the attribute is missing, the user is successfully\n authenticated.</div>",
      tags = "OAuth 2.0/OIDC",
      summary = "Check auth response"
   )
   @Path("authorization/check")
   @POST
   @Consumes("application/json")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response verifyAuthorizationResponse(
      @Parameter(description = "Containing the query string of the authorization response.") @NotNull @Valid AuthenticationOAuth2AuthorizationResponseQueryRequest request
   ) {
      FlowResult result = ((OAuth2Authentication)this.oAuth2Authentication.get()).verifyAuthorizationResponse(new QueryString(request.getQueryString()));
      return this.mapper.toResponse(result);
   }
}
