package com.airlock.iam.flow.shared.domain.service.email;

import com.airlock.iam.base.api.domain.model.email.EmailAddress;
import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.api.domain.model.validation.GenericError;
import com.airlock.iam.base.api.domain.model.validation.ValidationResult;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.flow.api.domain.model.session.FlowAttributeRepository;
import com.airlock.iam.flow.shared.api.domain.model.item.info.UserDataItemInfo;
import com.airlock.iam.flow.shared.domain.model.item.email.EmailItemDefinition;
import com.airlock.iam.flow.shared.domain.model.step.email.EmailChangeDescription;
import com.airlock.iam.flow.shared.domain.service.user.EditableStringItemChangeService;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.List;
import java.util.Optional;

public final class EmailChangeServiceImpl implements EditableStringItemChangeService {
   private final Optional<EmailItemDefinition> emailItemDefinition;
   private final Provider<FlowAttributeRepository> attributes;

   @Inject
   public EmailChangeServiceImpl(Optional<EmailItemDefinition> emailItemDefinition, Provider<FlowAttributeRepository> attributes) {
      this.emailItemDefinition = emailItemDefinition;
      this.attributes = attributes;
   }

   @Override
   public boolean canHandle(String key) {
      return this.emailItemDefinition.<Boolean>map(item -> item.getFieldName().equals(key)).orElse(false);
   }

   @Override
   public ValidationResult handleItemChange(String key, Optional<Object> value) {
      if (this.canHandle(key) && !this.emailItemDefinition.isEmpty()) {
         ValidationResult validationResult = this.emailItemDefinition.get().validate(value.map(Object::toString));
         if (!validationResult.isValid()) {
            return validationResult;
         }

         if (value.isEmpty()) {
            ((FlowAttributeRepository)this.attributes.get()).putOrMergeAttribute(EmailChangeDescription.remove());
            return ValidationResult.valid();
         }

         Optional<EmailAddress> validAddress = EmailAddress.emailAddress(value.get().toString());
         if (validAddress.isEmpty()) {
            return this.emailItemDefinition.get().failedValidation(GenericError.WRONG_FORMAT);
         }

         ((FlowAttributeRepository)this.attributes.get()).putOrMergeAttribute(EmailChangeDescription.of(validAddress.get()));
         return ValidationResult.valid();
      } else {
         throw new FailureException("Should not be here because service cannot handle key " + LogUtil.quote(key));
      }
   }

   @Override
   public ValidationResult validateAllData() {
      if (this.emailItemDefinition.isEmpty()) {
         return ValidationResult.valid();
      }

      Optional<String> currentEmailAddress = this.currentEmailAddress();
      ValidationResult validationResult = this.emailItemDefinition.get().validate(currentEmailAddress);
      if (validationResult.isValid()) {
         this.saveInitialEmailAddressToChangeIfEmpty();
      }

      return validationResult;
   }

   private void saveInitialEmailAddressToChangeIfEmpty() {
      Optional<EmailAddress> initialEmailAddress = this.initialEmailAddress().flatMap(EmailAddress::emailAddress);
      if (this.emailChange().isEmpty() && initialEmailAddress.isPresent()) {
         ((FlowAttributeRepository)this.attributes.get()).putOrMergeAttribute(EmailChangeDescription.of(initialEmailAddress.get()));
      }
   }

   @Override
   public List<UserDataItemInfo<String>> getItemInfos() {
      return this.emailItemDefinition
         .<UserDataItemInfo<String>>map(definition -> definition.getItemInfo(this.currentEmailAddress()))
         .map(List::of)
         .orElseGet(List::of);
   }

   private Optional<String> currentEmailAddress() {
      return this.emailChange().isPresent() ? this.changedEmailAddress() : this.initialEmailAddress();
   }

   private Optional<String> changedEmailAddress() {
      return this.emailChange().flatMap(EmailChangeDescription::getEmailAddress).map(EmailAddress::value);
   }

   private Optional<String> initialEmailAddress() {
      return this.emailItemDefinition.flatMap(EmailItemDefinition::initialValue);
   }

   private Optional<EmailChangeDescription> emailChange() {
      return ((FlowAttributeRepository)this.attributes.get()).tryGetAttribute(EmailChangeDescription.class);
   }

   @SuppressFBWarnings(justification = "generated code")
   public static EmailChangeServiceImpl.EmailChangeServiceImplBuilder builder() {
      return new EmailChangeServiceImpl.EmailChangeServiceImplBuilder();
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class EmailChangeServiceImplBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private Optional<EmailItemDefinition> emailItemDefinition;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<FlowAttributeRepository> attributes;

      @SuppressFBWarnings(justification = "generated code")
      EmailChangeServiceImplBuilder() {
      }

      @SuppressFBWarnings(justification = "generated code")
      public EmailChangeServiceImpl.EmailChangeServiceImplBuilder emailItemDefinition(Optional<EmailItemDefinition> emailItemDefinition) {
         this.emailItemDefinition = emailItemDefinition;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public EmailChangeServiceImpl.EmailChangeServiceImplBuilder attributes(Provider<FlowAttributeRepository> attributes) {
         this.attributes = attributes;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public EmailChangeServiceImpl build() {
         return new EmailChangeServiceImpl(this.emailItemDefinition, this.attributes);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "EmailChangeServiceImpl.EmailChangeServiceImplBuilder(emailItemDefinition="
            + this.emailItemDefinition
            + ", attributes="
            + this.attributes
            + ")";
      }
   }
}
