package com.airlock.iam.oauth2.domain.model.authorization;

import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.elog.api.EDefaultLogType;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.flow.api.domain.model.session.FlowAttributeRepository;
import com.airlock.iam.flow.shared.domain.model.uitenantid.UiTenantId;
import com.airlock.iam.flow.shared.domain.model.uitenantid.UiTenantIdRule;
import com.airlock.iam.oauth2.api.domain.model.OAuth2ClientId;
import com.airlock.iam.oauth2.application.configuration.OAuth2ClientIdPatternUiTenantIdRuleConfig;
import com.airlock.iam.oauth2.domain.model.codegrant.OAuth2AuthorizationCodeGrantFlowSessionAttribute;
import com.airlock.iam.oauth2.domain.model.codegrant.OAuth2OidcAuthorizationRequestAttributes;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Optional;
import java.util.regex.Matcher;

@LogicForConfig(OAuth2ClientIdPatternUiTenantIdRuleConfig.class)
public final class OAuth2ClientIdPatternUiTenantIdRule implements UiTenantIdRule {
   private static final ELogger LOG = ELoggerFactory.getLogger(OAuth2ClientIdPatternUiTenantIdRule.class);
   private final Provider<FlowAttributeRepository> attributeRepository;
   private final Provider<OAuth2ClientIdPatternUiTenantIdRuleSettings> settings;

   @Inject
   public OAuth2ClientIdPatternUiTenantIdRule(
      Provider<FlowAttributeRepository> attributeRepository, Provider<OAuth2ClientIdPatternUiTenantIdRuleSettings> settings
   ) {
      this.attributeRepository = attributeRepository;
      this.settings = settings;
   }

   @Override
   public Optional<UiTenantId> getUiTenantId() {
      return ((FlowAttributeRepository)this.attributeRepository.get())
         .tryGetAttribute(OAuth2AuthorizationCodeGrantFlowSessionAttribute.class)
         .map(OAuth2AuthorizationCodeGrantFlowSessionAttribute::request)
         .map(OAuth2OidcAuthorizationRequestAttributes::clientId)
         .flatMap(this::uiTenantIdValue);
   }

   private Optional<UiTenantId> uiTenantIdValue(OAuth2ClientId clientId) {
      Matcher matcher = ((OAuth2ClientIdPatternUiTenantIdRuleSettings)this.settings.get()).clientIdPattern().matcher(clientId.id());
      if (!matcher.matches()) {
         return Optional.empty();
      }

      try {
         return Optional.of(matcher.replaceFirst(((OAuth2ClientIdPatternUiTenantIdRuleSettings)this.settings.get()).uiTenantIdValue())).map(UiTenantId::new);
      } catch (IndexOutOfBoundsException e) {
         LOG.warn(
            EDefaultLogType.GENERIC_LOG_TYPE,
            "The configured UI tenant ID value contains a back-reference that is not consistent with the configured client ID pattern."
         );
         return Optional.empty();
      }
   }
}
