package com.airlock.iam.login.rest.infrastructure.resource.prot;

import ch.ergon.restal.jsonapi.document.data.ResourceDocument;
import ch.ergon.restal.jsonapi.document.data.ResourceObject;
import com.airlock.iam.common.api.domain.model.contextdata.ContextDataItem;
import com.airlock.iam.common.api.domain.model.store.user.UserStore;
import com.airlock.iam.common.api.domain.model.user.PersistentUser;
import com.airlock.iam.common.api.domain.model.user.User;
import com.airlock.iam.common.api.infrastructure.annotation.RequiresWritableSession;
import com.airlock.iam.common.domain.model.user.PrincipalInfo;
import com.airlock.iam.login.rest.infrastructure.generated.transfer.shared.response.my.UserDataResourceDocument;
import com.airlock.iam.login.rest.infrastructure.service.user.UserSelfInformationService;
import com.airlock.iam.login.rest.infrastructure.transfer.shared.response.my.UserData;
import io.swagger.v3.oas.annotations.Operation;
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.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Response;
import java.util.HashMap;
import java.util.Map;
import ma.glasnost.orika.MapperFacade;

@Path("/my/self")
public class UserSelfResource {
   private final Provider<UserSelfInformationService> contextDataService;
   private final MapperFacade mapper;
   private final Provider<UserStore> userStore;
   private final Provider<PrincipalInfo> principalInfo;

   @Inject
   public UserSelfResource(
      MapperFacade mapper, Provider<UserSelfInformationService> contextDataService, Provider<UserStore> userStore, Provider<PrincipalInfo> principalInfo
   ) {
      this.contextDataService = contextDataService;
      this.mapper = mapper;
      this.userStore = userStore;
      this.principalInfo = principalInfo;
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "Retrieved the authenticated user.",
               content = {
                     @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = UserDataResourceDocument.class)),
                     @Content(mediaType = "application/json", schema = @Schema(implementation = UserDataResourceDocument.class))
               }
            ),
            @ApiResponse(
               responseCode = "401",
               description = "Invalid or missing authentication.",
               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"))
               }
            ),
            @ApiResponse(
               responseCode = "403",
               description = "Access to the requested service is forbidden. Authentication will not help.",
               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 = "Retrieves the authenticated user.\n<div class=\"iam-resource-return\">The available information about the authenticated user.</div>",
      tags = "User",
      summary = "Get user"
   )
   @GET
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   @RequiresWritableSession
   public Response retrieveSelf() {
      User self = ((UserSelfInformationService)this.contextDataService.get()).loadUserWithConfiguredContextData(this.authenticatedUser().getName());
      UserData userData = this.convertToUserData(self);
      ResourceDocument<UserData> document = ResourceObject.builder(this.authenticatedUser().getName(), userData).document();
      return Response.ok(document).build();
   }

   private UserData convertToUserData(User self) {
      UserData userData = (UserData)this.mapper.map(this.authenticatedUser(), UserData.class);
      return this.addContextDataToUserData(self, userData);
   }

   private UserData addContextDataToUserData(User self, UserData userData) {
      Map<String, Object> contextData = new HashMap<>();

      for (ContextDataItem<?> contextDataItem : self.getContextData().getItems()) {
         contextData.put(contextDataItem.getKey(), contextDataItem.getValue());
      }

      userData.setContextData(contextData);
      return userData;
   }

   private PersistentUser authenticatedUser() {
      return ((UserStore)this.userStore.get()).getByIdentity(((PrincipalInfo)this.principalInfo.get()).tryGetAuthenticatedIdentity().id());
   }
}
