package com.airlock.iam.oauth2.application.service;

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.common.api.domain.model.user.UserIdentity;
import com.airlock.iam.common.application.service.session.AirlockControlCookieService;
import com.airlock.iam.common.domain.model.user.PrincipalInfo;
import com.airlock.iam.oauth2.api.domain.model.OAuth2Scope;
import com.airlock.iam.oauth2.domain.model.consent.OAuth2RemoteConsentSettings;
import com.airlock.iam.oauth2.domain.model.provider.OAuth2ConsentClientProvider;
import com.airlock.iam.oauth2.domain.model.provider.OAuth2ConsentStepSession;
import com.airlock.iam.oauth2.domain.model.remoteconsent.RemoteConsentEncoder;
import com.airlock.iam.oauth2.domain.model.remoteconsent.RemoteConsentRequestNonce;
import com.airlock.iam.oauth2.domain.model.remoteconsent.RemoteConsentRequestToken;
import com.airlock.iam.oauth2.domain.model.remoteconsent.RemoteConsentSessionNonce;
import com.airlock.iam.oauth2.domain.model.remoteconsent.RemoteConsentVerificationResult;
import com.airlock.iam.oauth2.domain.model.remoteconsent.RemoteConsentVerifier;
import com.airlock.iam.oauth2.domain.service.consent.OAuth2RemoteConsentService;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.List;
import java.util.Optional;

public final class OAuth2RemoteConsentServiceImpl implements OAuth2RemoteConsentService {
   private final Provider<OAuth2RemoteConsentSettings> settings;
   private final Provider<AirlockControlCookieService> controlCookieService;
   private final Provider<OAuth2ConsentClientProvider> clientProvider;
   private final Provider<OAuth2ConsentStepSession> stepSession;
   private final Provider<RemoteConsentEncoder> consentEncoder;
   private final Provider<RemoteConsentVerifier> consentVerifier;
   private final Provider<PrincipalInfo> user;

   @Inject
   public OAuth2RemoteConsentServiceImpl(
      Provider<OAuth2RemoteConsentSettings> settings,
      Provider<AirlockControlCookieService> controlCookieService,
      Provider<OAuth2ConsentClientProvider> clientProvider,
      Provider<OAuth2ConsentStepSession> stepSession,
      Provider<RemoteConsentEncoder> consentEncoder,
      Provider<RemoteConsentVerifier> consentVerifier,
      Provider<PrincipalInfo> user
   ) {
      this.settings = settings;
      this.controlCookieService = controlCookieService;
      this.clientProvider = clientProvider;
      this.stepSession = stepSession;
      this.consentEncoder = consentEncoder;
      this.consentVerifier = consentVerifier;
      this.user = user;
   }

   @Override
   public Optional<RemoteConsentVerificationResult> check(String consentToken) {
      Optional<OAuth2ConsentStepSession.RemoteConsentSessionData> sessionData = ((OAuth2ConsentStepSession)this.stepSession.get())
         .getRemoteConsentSessionData();
      if (sessionData.isEmpty()) {
         return Optional.empty();
      }

      RemoteConsentVerificationResult verificationResult = ((RemoteConsentVerifier)this.consentVerifier.get())
         .verify(consentToken, sessionData.get().getNonce());
      return Optional.ofNullable(verificationResult);
   }

   @Override
   public RemoteConsentEncoder.JwtEncoderResult createConsentToken(List<OAuth2Scope> requestedScopes) {
      RemoteConsentRequestToken requestToken = RemoteConsentRequestToken.builder()
         .userId(this.userId())
         .scope(requestedScopes)
         .callbackUri(this.settings().callbackUri())
         .clientId(((OAuth2ConsentClientProvider)this.clientProvider.get()).provide().clientId())
         .validity(this.settings().validity())
         .build();
      RemoteConsentRequestNonce requestNonce = new RemoteConsentRequestNonce();
      Optional<RemoteConsentEncoder.JwtEncoderResult> jwtEncoderResult = ((RemoteConsentEncoder)this.consentEncoder.get()).encode(requestToken, requestNonce);
      if (jwtEncoderResult.isEmpty()) {
         throw new FailureException("Failed to create consent token");
      }

      OAuth2ConsentStepSession.RemoteConsentSessionData sessionData = new OAuth2ConsentStepSession.RemoteConsentSessionData(
         new RemoteConsentSessionNonce(requestNonce)
      );
      ((OAuth2ConsentStepSession)this.stepSession.get()).setRemoteConsentSessionData(sessionData);
      this.settings()
         .wafCredential()
         .ifPresent(c -> ((AirlockControlCookieService)this.controlCookieService.get()).addCredentialsWithKeepSessionId(new String[]{c.toString()}));
      return jwtEncoderResult.get();
   }

   private OAuth2RemoteConsentSettings settings() {
      return (OAuth2RemoteConsentSettings)this.settings.get();
   }

   private String userId() {
      return ((PrincipalInfo)this.user.get()).getKnownIdentity().map(UserIdentity::id).map(UserId::getValue).orElseThrow();
   }
}
