package com.airlock.iam.common.domain.model.sso;

import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.base.misc.result.Result;
import com.airlock.iam.common.api.domain.model.authentication.Authentee;
import com.airlock.iam.common.api.domain.model.authentication.AuthenticationResult;
import com.airlock.iam.common.api.domain.model.credential.RequestAuthentication;
import com.airlock.iam.common.api.domain.model.request.Request;
import com.airlock.iam.common.api.domain.model.user.ProvidedUsername;
import com.airlock.iam.common.api.domain.model.user.transformation.UsernameTransformation;
import com.airlock.iam.common.application.configuration.credential.SsoTicketRequestAuthenticationConfig;
import com.airlock.iam.common.domain.model.authentication.AuthenticationResultImpl;
import com.airlock.iam.common.domain.service.sso.SsoTicketService;
import com.airlock.iam.common.domain.service.username.UsernameValidationService;
import com.airlock.iam.core.domain.model.authentication.factor.AuthenticationFactorUsageImpl;
import com.airlock.iam.core.domain.model.authentication.factor.KnownAuthenticationFactor;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Optional;

@LogicForConfig(SsoTicketRequestAuthenticationConfig.class)
public final class SsoTicketRequestAuthentication implements RequestAuthentication {
   private static final ELogger LOG = ELoggerFactory.getLogger(SsoTicketRequestAuthentication.class);
   private final Provider<SsoTicketExtractor> ssoTicketExtractor;
   private final Provider<UsernameTransformation> usernameTransformation;
   private final Provider<SsoTicketService> ssoTicketService;
   private final Provider<UsernameValidationService> usernameValidationService;

   @Inject
   public SsoTicketRequestAuthentication(
      Provider<SsoTicketExtractor> ssoTicketExtractor,
      Provider<UsernameTransformation> usernameTransformation,
      Provider<SsoTicketService> ssoTicketService,
      Provider<UsernameValidationService> usernameValidationService
   ) {
      this.ssoTicketExtractor = ssoTicketExtractor;
      this.usernameTransformation = usernameTransformation;
      this.ssoTicketService = ssoTicketService;
      this.usernameValidationService = usernameValidationService;
   }

   @Override
   public Optional<AuthenticationResult> extractCredentialAndAuthenticate(Request request) {
      Optional<String> ticketString = ((SsoTicketExtractor)this.ssoTicketExtractor.get()).extract();
      if (ticketString.isEmpty()) {
         return Optional.empty();
      }

      Result<SsoTicketService.SsoTicket, SsoTicketService.SsoTicketError> ticket = ((SsoTicketService)this.ssoTicketService.get()).extractSsoTicket();
      if (ticket.isFailure()) {
         return ticket.getFailure()
            .userIdentity()
            .map(
               id -> AuthenticationResultImpl.failedAuthenticationWithoutFactorCheck(
                  id, AuthenticationFactorUsageImpl.failedFactorUsage(KnownAuthenticationFactor.TOKEN)
               )
            );
      }

      ProvidedUsername providedUsername = ticket.get().username();
      if (!((UsernameValidationService)this.usernameValidationService.get()).isValid(providedUsername)) {
         LOG.info("Provided username rejected by global username filter.");
         return Optional.empty();
      }

      Optional<SsoTicketService.ExtractionResult> extractionResult = ((SsoTicketService)this.ssoTicketService.get())
         .extractAuthentee(ticket.get(), (UsernameTransformation)this.usernameTransformation.get());
      if (extractionResult.isEmpty()) {
         return Optional.empty();
      }

      Authentee authentee = extractionResult.get().authentee();
      return Optional.of(
         AuthenticationResultImpl.successfulAuthenticationWithFactorCheck(
            authentee, AuthenticationFactorUsageImpl.succeededFactorUsage(KnownAuthenticationFactor.TOKEN)
         )
      );
   }
}
