package com.airlock.iam.login.domain.model.selection.condition;

import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.common.api.domain.model.device.DeviceType;
import com.airlock.iam.common.api.domain.model.device.usage.AuthenticationDeviceUsage;
import com.airlock.iam.common.api.domain.model.user.UserIdentity;
import com.airlock.iam.common.domain.model.device.usage.repository.DeviceUsageRepository;
import com.airlock.iam.common.domain.model.user.PrincipalInfo;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.elog.api.LogTypes;
import com.airlock.iam.flow.api.domain.model.step.FlowCondition;
import com.airlock.iam.flow.shared.api.domain.service.FlowAttributeService;
import com.airlock.iam.flow.shared.application.configuration.device.usage.FirstDeviceUsageConditionConfig;
import com.airlock.iam.flow.shared.domain.model.attributes.AuthenticationDeviceUsageAttribute;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Optional;

@LogicForConfig(FirstDeviceUsageConditionConfig.class)
public final class FirstDeviceUsageCondition implements FlowCondition {
   private static final ELogger LOG = ELoggerFactory.getLogger(FirstDeviceUsageCondition.class);
   private final Provider<PrincipalInfo> user;
   private final Provider<Optional<DeviceUsageRepository>> repository;
   private final Provider<Optional<DeviceType>> deviceType;
   private final Provider<FlowAttributeService> attributes;

   @Inject
   public FirstDeviceUsageCondition(
      Provider<PrincipalInfo> user,
      Provider<Optional<DeviceUsageRepository>> repository,
      Provider<Optional<DeviceType>> deviceType,
      Provider<FlowAttributeService> attributes
   ) {
      this.user = user;
      this.repository = repository;
      this.deviceType = deviceType;
      this.attributes = attributes;
   }

   @Override
   public boolean isFulfilled() {
      Optional<UserId> userId = ((PrincipalInfo)this.user.get()).getKnownIdentity().map(UserIdentity::id);
      if (userId.isEmpty()) {
         LOG.debug("User not identified - no device usages can be determined");
         return false;
      } else if (((Optional)this.repository.get()).isEmpty()) {
         LOG.warn(LogTypes.CONFIGURATION_ERROR, "Device Usage Repository not configured - no device usages can be determined. Condition evaluating to false.");
         return false;
      } else {
         Optional<AuthenticationDeviceUsage> usedDevice = ((FlowAttributeService)this.attributes.get())
            .findAttribute(AuthenticationDeviceUsageAttribute.class)
            .stream()
            .flatMap(authenticationDeviceUsageAttribute -> authenticationDeviceUsageAttribute.getUsedDevices().stream())
            .filter(this::filterDeviceType)
            .reduce((first, second) -> second);
         if (usedDevice.isEmpty()) {
            LOG.debug(
               String.format(
                  "No device usages of type %s in current flow. Condition evaluating to false.",
                  LogUtil.logSafeQuote(((Optional)this.deviceType.get()).map(DeviceType::id).orElse("any"))
               )
            );
            return false;
         } else {
            return usedDevice.flatMap(
                  device -> ((DeviceUsageRepository)((Optional)this.repository.get()).orElseThrow())
                     .getDeviceUsage(userId.get(), device.deviceType().id(), device.deviceId().id())
               )
               .isEmpty();
         }
      }
   }

   private boolean filterDeviceType(AuthenticationDeviceUsage usage) {
      return ((Optional)this.deviceType.get()).<Boolean>map(type -> usage.deviceType().id().equals(type.id())).orElse(true);
   }

   @Override
   public String displayName() {
      return "First Device Usage";
   }
}
