package com.airlock.iam.flow.shared.infrastructure.session;

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.api.domain.model.mapification.MapifiedValues;
import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.base.domain.model.mapification.MapificationUtils;
import com.airlock.iam.base.domain.model.mapification.MapifiedValuesBuilder;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.common.api.domain.model.user.UserIdentity;
import com.airlock.iam.common.domain.service.mapification.Mapifier;
import com.airlock.iam.flow.api.domain.model.redflag.RedFlag;
import com.airlock.iam.flow.application.configuration.flow.FlowConfigHash;
import com.airlock.iam.flow.domain.model.session.FlowSessionStatus;
import com.airlock.iam.flow.domain.model.session.InternalFlowSession;
import com.airlock.iam.flow.shared.domain.model.session.FlowAttributeRepositoryMapifier;
import com.airlock.iam.flow.shared.domain.model.session.FlowSessionImpl;
import com.airlock.iam.flow.shared.domain.model.session.FlowSessionMapifier;
import com.airlock.iam.flow.shared.domain.model.session.FlowSessionTagsMapifier;
import com.airlock.iam.flow.shared.domain.model.session.StepResultMapifier;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.ArrayDeque;
import java.util.Optional;
import java.util.stream.Collectors;

public final class FlowSessionMapifierImpl implements FlowSessionMapifier {
   private static final String USER_IDENTITY_KEY = "userId";
   private static final String STATUS_KEY = "status";
   private static final String FLOW_CONFIG_HASH_KEY = "flowCHash";
   private static final String FLOW_ATTRIBUTES_KEY = "flowAttrs";
   private static final String FLOW_SESSION_TAGS_KEY = "flowSessTags";
   private static final String RED_FLAGS_KEY = "redFlags";
   private static final String MUST_FINALIZE_CURRENT_STEP_KEY = "mustFinalize";
   private static final String STEP_RESULT_HISTORY_KEY = "stepResultHist";
   private static final String CURRENT_STEP_SESSION_KEY = "stepSession";
   private final Provider<FlowSessionTagsMapifier> flowSessionTagsMapifier;
   private final Provider<FlowAttributeRepositoryMapifier> attributesMapifier;
   private final Provider<Mapifier> mapifier;
   private final Provider<StepResultMapifier> stepResultMapifier;

   @Inject
   public FlowSessionMapifierImpl(
      Provider<FlowSessionTagsMapifier> flowSessionTagsMapifier,
      Provider<FlowAttributeRepositoryMapifier> attributesMapifier,
      Provider<StepResultMapifier> stepResultMapifier,
      Provider<Mapifier> mapifier
   ) {
      this.flowSessionTagsMapifier = flowSessionTagsMapifier;
      this.attributesMapifier = attributesMapifier;
      this.stepResultMapifier = stepResultMapifier;
      this.mapifier = mapifier;
   }

   @Override
   public MapifiedValues mapifyFlowSession(InternalFlowSession session) {
      if (session instanceof FlowSessionImpl flowSession) {
         return MapifiedValuesBuilder.mapifiedValues()
            .putIfPresent("userId", flowSession.getUserIdentity().map(UserIdentityMapifier::mapifyUserIdentity))
            .put("status", flowSession.getStatus().name())
            .put("flowCHash", flowSession.getFlowConfigHash().getFlowConfigHashCode())
            .put("flowSessTags", ((FlowSessionTagsMapifier)this.flowSessionTagsMapifier.get()).mapify(flowSession.getFlowSessionTags()))
            .putIfNotEmpty("redFlags", MapificationUtils.mapifyCollection(flowSession.getRedFlags(), RedFlag::getName))
            .put("mustFinalize", flowSession.mustFinalizeCurrentStep())
            .putIfNotEmpty("flowAttrs", ((FlowAttributeRepositoryMapifier)this.attributesMapifier.get()).mapify(flowSession.flowAttributes()))
            .putIfNotEmpty(
               "stepResultHist",
               MapificationUtils.mapifyCollection(session.getStepResultHistory(), ((StepResultMapifier)this.stepResultMapifier.get())::mapify)
            )
            .putIfPresent("stepSession", flowSession.getCurrentStepSession().map(((Mapifier)this.mapifier.get())::mapify))
            .build();
      } else {
         throw new FailureException("Unknown " + LogUtil.quote(InternalFlowSession.class.getName()) + " class: " + LogUtil.quote(session.getClass().getName()));
      }
   }

   @Override
   public InternalFlowSession demapifyFlowSession(MapifiedValues values) {
      return FlowSessionImpl.restoreFlowSession(
         FlowSessionStatus.valueOf(values.get("status")),
         FlowConfigHash.of(values.<Integer>get("flowCHash")),
         ((FlowSessionTagsMapifier)this.flowSessionTagsMapifier.get()).demapify(values.getValues("flowSessTags")),
         values.demapifyOptional("userId", UserIdentityMapifier::demapifyUserIdentity),
         values.<String>getList("redFlags").stream().map(RedFlag::new).collect(Collectors.toSet()),
         new ArrayDeque<>(values.demapifyList("stepResultHist", ((StepResultMapifier)this.stepResultMapifier.get())::demapify)),
         ((FlowAttributeRepositoryMapifier)this.attributesMapifier.get()).demapify(values.getList("flowAttrs")),
         values.demapifyOptional("stepSession", ((Mapifier)this.mapifier.get())::demapify).orElse(null),
         values.<Boolean>get("mustFinalize")
      );
   }

   public static Optional<UserId> extractUserId(MapifiedValues flowSessionValues) {
      return flowSessionValues.demapifyOptional("userId", UserIdentityMapifier::demapifyUserIdentity).map(UserIdentity::id);
   }

   @Override
   public boolean containsActiveFlowSession(MapifiedValues flowSessionValues) {
      Optional<String> flowSessionStatus = flowSessionValues.getOptional("status");
      if (flowSessionStatus.isEmpty()) {
         return true;
      }

      try {
         FlowSessionStatus status = FlowSessionStatus.valueOf(flowSessionStatus.get());
         return status.isBefore(FlowSessionStatus.TERMINATED);
      } catch (IllegalArgumentException e) {
         return true;
      }
   }
}
