package com.airlock.iam.oauth2.application.service;

import com.airlock.iam.base.api.domain.model.validation.ValidationResult;
import com.airlock.iam.oauth2.domain.model.session.OAuth2CustomSessionAttribute;
import com.airlock.iam.oauth2.domain.model.session.OAuth2CustomSessionAttributeValidator;
import com.airlock.iam.oauth2.domain.model.session.OAuth2ExtendedSession;
import jakarta.annotation.Nullable;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class OAuth2SessionAttributeUpdateService {
   private final Provider<OAuth2CustomSessionAttributeValidator> attributeValidator;

   @Inject
   public OAuth2SessionAttributeUpdateService(Provider<OAuth2CustomSessionAttributeValidator> attributeValidator) {
      this.attributeValidator = attributeValidator;
   }

   public ValidationResult validateAndExecuteUpdateRequest(
      OAuth2ExtendedSession session, List<OAuth2SessionAttributeUpdateService.AttributeUpdate> updatedCustomAttributes
   ) {
      ValidationResult deleteAttributesValidationResult = this.deleteAttributes(session, updatedCustomAttributes);
      if (deleteAttributesValidationResult.isNotValid()) {
         return deleteAttributesValidationResult;
      }

      ValidationResult deleteAttributesToUpdateValidationResult = this.deleteAttributesToUpdate(session, updatedCustomAttributes);
      if (deleteAttributesToUpdateValidationResult.isNotValid()) {
         return deleteAttributesToUpdateValidationResult;
      }

      ValidationResult validationResultForAddition = this.addNewAttributes(session, updatedCustomAttributes);
      return validationResultForAddition.isNotValid() ? validationResultForAddition : ValidationResult.valid();
   }

   private ValidationResult deleteAttributes(OAuth2ExtendedSession session, List<OAuth2SessionAttributeUpdateService.AttributeUpdate> updatedCustomAttributes) {
      Set<String> attributesToDelete = updatedCustomAttributes.stream()
         .filter(OAuth2SessionAttributeUpdateService.AttributeUpdate::isDeleteRequest)
         .map(OAuth2SessionAttributeUpdateService.AttributeUpdate::key)
         .collect(Collectors.toSet());
      if (attributesToDelete.isEmpty()) {
         return ValidationResult.valid();
      } else {
         ValidationResult validationResult = ((OAuth2CustomSessionAttributeValidator)this.attributeValidator.get())
            .validateDeleteAttribute(attributesToDelete, session.customAttributes());
         if (validationResult.isValid()) {
            session.deleteCustomAttributes(attributesToDelete);
            return ValidationResult.valid();
         } else {
            return validationResult;
         }
      }
   }

   private ValidationResult deleteAttributesToUpdate(
      OAuth2ExtendedSession session, List<OAuth2SessionAttributeUpdateService.AttributeUpdate> updatedCustomAttributes
   ) {
      Set<String> attributesToDelete = updatedCustomAttributes.stream()
         .filter(Predicate.not(OAuth2SessionAttributeUpdateService.AttributeUpdate::isDeleteRequest))
         .map(OAuth2SessionAttributeUpdateService.AttributeUpdate::key)
         .filter(session::hasCustomAttribute)
         .collect(Collectors.toSet());
      if (attributesToDelete.isEmpty()) {
         return ValidationResult.valid();
      } else {
         ValidationResult validationResult = ((OAuth2CustomSessionAttributeValidator)this.attributeValidator.get())
            .validateDeleteAttribute(attributesToDelete, session.customAttributes());
         if (validationResult.isValid()) {
            session.deleteCustomAttributes(attributesToDelete);
            return ValidationResult.valid();
         } else {
            return validationResult;
         }
      }
   }

   private ValidationResult addNewAttributes(OAuth2ExtendedSession session, List<OAuth2SessionAttributeUpdateService.AttributeUpdate> updatedCustomAttributes) {
      Set<OAuth2CustomSessionAttribute> newAttributes = updatedCustomAttributes.stream()
         .filter(Predicate.not(OAuth2SessionAttributeUpdateService.AttributeUpdate::isDeleteRequest))
         .map(OAuth2SessionAttributeUpdateService::createOAuth2CustomSessionAttribute)
         .collect(Collectors.toSet());
      if (newAttributes.isEmpty()) {
         return ValidationResult.valid();
      } else {
         ValidationResult validationResult = ((OAuth2CustomSessionAttributeValidator)this.attributeValidator.get())
            .validateAddAttribute(newAttributes, session.customAttributes());
         if (validationResult.isValid()) {
            session.addCustomAttributes(newAttributes);
            return ValidationResult.valid();
         } else {
            return validationResult;
         }
      }
   }

   private static OAuth2CustomSessionAttribute createOAuth2CustomSessionAttribute(OAuth2SessionAttributeUpdateService.AttributeUpdate entry) {
      return OAuth2CustomSessionAttribute.builder().name(entry.key()).value(Objects.requireNonNull(entry.value())).build();
   }

   public record AttributeUpdate(String key, @Nullable String value) {
      public boolean isDeleteRequest() {
         return this.value == null;
      }
   }
}
