package com.airlock.iam.oauth2.domain.service.consent;

import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.common.domain.model.user.PrincipalInfo;
import com.airlock.iam.oauth2.api.domain.model.AuthorizationServerId;
import com.airlock.iam.oauth2.api.domain.model.OAuth2ClientId;
import com.airlock.iam.oauth2.api.domain.model.OAuth2Scope;
import com.airlock.iam.oauth2.application.configuration.consentstorage.OAuth2ConsentStorageConfig;
import com.airlock.iam.oauth2.domain.model.consent.OAuth2Consent;
import com.airlock.iam.oauth2.domain.model.consent.OAuth2ConsentId;
import com.airlock.iam.oauth2.domain.model.consent.OAuth2ConsentRepository;
import com.airlock.iam.oauth2.domain.model.consent.OAuth2ConsentStatus;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.time.Instant;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;

@LogicForConfig(OAuth2ConsentStorageConfig.class)
public class OAuth2LocalConsentStorageServiceImpl implements OAuth2LocalConsentStorageService {
   private final Provider<OAuth2ConsentRepository> consentRepository;
   private final Provider<PrincipalInfo> principalInfo;

   @Inject
   public OAuth2LocalConsentStorageServiceImpl(Provider<OAuth2ConsentRepository> consentRepository, Provider<PrincipalInfo> principalInfo) {
      this.consentRepository = consentRepository;
      this.principalInfo = principalInfo;
   }

   @Override
   public Collection<OAuth2Consent> findSavedConsents(AuthorizationServerId authorizationServerId, OAuth2ClientId clientId) {
      return ((OAuth2ConsentRepository)this.consentRepository.get())
         .findByUserAndAuthorizationServerAndClient(this.getUserId(), authorizationServerId, clientId);
   }

   @Override
   public void saveScopes(AuthorizationServerId authorizationServerId, OAuth2ClientId clientId, Set<String> grantedScopes, Set<String> deniedScopes) {
      List<OAuth2Consent> consents = Stream.concat(
            this.consentsWithStatus(authorizationServerId, clientId, grantedScopes, OAuth2ConsentStatus.GRANTED),
            this.consentsWithStatus(authorizationServerId, clientId, deniedScopes, OAuth2ConsentStatus.DENIED)
         )
         .toList();
      ((OAuth2ConsentRepository)this.consentRepository.get()).insertOrUpdateConsents(consents);
   }

   private UserId getUserId() {
      return ((PrincipalInfo)this.principalInfo.get()).getKnownIdentity().orElseThrow().id();
   }

   private Stream<OAuth2Consent> consentsWithStatus(
      AuthorizationServerId authorizationServerId, OAuth2ClientId clientId, Set<String> scopes, OAuth2ConsentStatus consentStatus
   ) {
      return scopes.stream()
         .map(
            scope -> new OAuth2Consent(
               OAuth2ConsentId.generateOAuth2ConsentId(),
               clientId,
               authorizationServerId,
               this.getUserId(),
               OAuth2Scope.of(scope),
               consentStatus,
               Instant.now()
            )
         );
   }
}
