package com.airlock.iam.flow.impl.domain.model.step;

import com.airlock.iam.flow.api.domain.model.step.AdditionalAttributes;
import com.airlock.iam.flow.api.domain.model.step.InternalStepResultData;
import com.airlock.iam.flow.api.domain.model.step.StepAction;
import com.airlock.iam.flow.api.domain.model.step.StepError;
import com.airlock.iam.flow.api.domain.model.step.StepResult;
import com.airlock.iam.flow.api.domain.model.step.StepResultBuilder;
import com.airlock.iam.flow.api.domain.model.step.StepResultData;
import com.airlock.iam.flow.api.domain.model.tag.Tag;
import jakarta.annotation.Nullable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

public final class DefaultStepResultBuilder implements StepResultBuilder {
   private StepResult.StepResultType type;
   private Optional<StepAction> nextAction = Optional.empty();
   private Optional<StepError> error = Optional.empty();
   private Optional<? extends StepResultData> resultData = Optional.empty();
   private Optional<? extends InternalStepResultData> internalResultData = Optional.empty();
   private final Set<Tag> obtainedTags = new HashSet<>();
   private final Map<String, Object> metadata = new HashMap<>();
   private AdditionalAttributes additionalAttributes = new AdditionalAttributes();

   public static DefaultStepResultBuilder defaultStepResultBuilder(StepResult.StepResultType type) {
      Objects.requireNonNull(type);
      return new DefaultStepResultBuilder(type);
   }

   private DefaultStepResultBuilder(StepResult.StepResultType type) {
      this.type = type;
   }

   @Override
   public StepResultBuilder type(StepResult.StepResultType value) {
      Objects.requireNonNull(value);
      this.type = value;
      return this;
   }

   @Override
   public StepResultBuilder nextAction(@Nullable StepAction value) {
      this.nextAction = Optional.ofNullable(value);
      return this;
   }

   @Override
   public StepResultBuilder error(StepError value) {
      this.error = Optional.of(value);
      return this;
   }

   @Override
   public StepResultBuilder obtainedTags(Set<Tag> newTags) {
      this.obtainedTags.clear();
      this.obtainedTags.addAll(newTags);
      return this;
   }

   @Override
   public StepResultBuilder addObtainedTags(Set<Tag> additionalTags) {
      this.obtainedTags.addAll(additionalTags);
      return this;
   }

   @Override
   public StepResultBuilder resultData(StepResultData value) {
      this.resultData = Optional.of(value);
      return this;
   }

   @Override
   public StepResultBuilder internalResultData(InternalStepResultData internalResultData) {
      this.internalResultData = Optional.of(internalResultData);
      return this;
   }

   @Override
   public StepResultBuilder withoutInternalResultData() {
      this.internalResultData = Optional.empty();
      return this;
   }

   @Override
   public StepResultBuilder metadata(Map<String, Object> value) {
      this.metadata.clear();
      this.metadata.putAll(value);
      return this;
   }

   @Override
   public StepResultBuilder addMetadataItem(String key, Object value) {
      this.metadata.put(key, value);
      return this;
   }

   @Override
   public StepResultBuilder additionalAttributes(AdditionalAttributes attributes) {
      this.additionalAttributes = attributes;
      return this;
   }

   @Override
   public StepResultBuilder addAdditionalAttribute(String key, Object value) {
      this.additionalAttributes.put(key, value);
      return this;
   }

   @Override
   public StepResult build() {
      return new StepResultImpl(
         this.type, this.nextAction, this.error, this.obtainedTags, this.resultData, this.internalResultData, this.metadata, this.additionalAttributes
      );
   }
}
