package com.airlock.iam.admin.infrastructure.factory;

import com.airlock.iam.admin.api.domain.service.user.ManagedUserRepository;
import com.airlock.iam.admin.application.configuration.generic.GenericTokenControllerConfig;
import com.airlock.iam.admin.application.service.token.generic.GenericTokenEventService;
import com.airlock.iam.admin.application.service.token.generic.GenericTokenReadOnlyAttributeValidator;
import com.airlock.iam.admin.application.service.token.generic.GenericTokenService;
import com.airlock.iam.admin.application.service.token.generic.NoTokenEventsService;
import com.airlock.iam.admin.application.service.token.generic.device.DeviceTokenEventsService;
import com.airlock.iam.admin.application.service.token.generic.email.EmailTokenEventsService;
import com.airlock.iam.admin.domain.model.authmethod.AuthenticationMethod;
import com.airlock.iam.admin.domain.model.token.generic.GenericTokenEndpoint;
import com.airlock.iam.base.api.domain.model.exception.ServiceConfigurationException;
import com.airlock.iam.base.api.domain.model.validation.Validator;
import com.airlock.iam.common.domain.model.device.event.DeviceTokenEventFactory;
import com.airlock.iam.common.domain.model.event.EmailAddressChangesEventFactory;
import com.airlock.iam.log.api.signing.SigningLogger;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.List;
import java.util.Map;

public class TokenServiceFactory {
   private final Provider<List<GenericTokenEndpoint>> tokenEndpoints;
   private final Provider<ManagedUserRepository> userRepository;
   private final Provider<EmailAddressChangesEventFactory> emailEventFactory;
   private final Provider<DeviceTokenEventFactory> deviceTokenEventFactory;
   private final SigningLogger signingLogger;

   @Inject
   public TokenServiceFactory(
      Provider<List<GenericTokenEndpoint>> tokenEndpoints,
      Provider<ManagedUserRepository> userRepository,
      Provider<EmailAddressChangesEventFactory> emailEventFactory,
      Provider<DeviceTokenEventFactory> deviceTokenEventFactory,
      SigningLogger signingLogger
   ) {
      this.tokenEndpoints = tokenEndpoints;
      this.userRepository = userRepository;
      this.emailEventFactory = emailEventFactory;
      this.deviceTokenEventFactory = deviceTokenEventFactory;
      this.signingLogger = signingLogger;
   }

   public GenericTokenService getServiceForType(AuthenticationMethod.TokenType tokenType) {
      GenericTokenEndpoint endpoint = this.getEndpointForType(tokenType);
      return GenericTokenService.builder()
         .signingLogger(this.signingLogger)
         .tokenRepository(endpoint.getTokenRepository())
         .userRepository((ManagedUserRepository)this.userRepository.get())
         .attributeMapping(this.getEndpointForType(tokenType).getAttributeMapping())
         .attributeValidator(this.createValidator(tokenType))
         .tokenType(tokenType)
         .eventService(this.createEventServiceForType(endpoint.getTokenEventType()))
         .build();
   }

   private GenericTokenEventService createEventServiceForType(GenericTokenControllerConfig.TokenEventType eventType) {
      if (eventType == GenericTokenControllerConfig.TokenEventType.EMAIL) {
         return new EmailTokenEventsService((EmailAddressChangesEventFactory)this.emailEventFactory.get());
      } else {
         return eventType == GenericTokenControllerConfig.TokenEventType.DEVICE_TOKEN
            ? new DeviceTokenEventsService((DeviceTokenEventFactory)this.deviceTokenEventFactory.get())
            : new NoTokenEventsService();
      }
   }

   private Validator<Map<String, Object>> createValidator(AuthenticationMethod.TokenType tokenType) {
      return new GenericTokenReadOnlyAttributeValidator(this.getEndpointForType(tokenType).getAttributeMapping());
   }

   private GenericTokenEndpoint getEndpointForType(AuthenticationMethod.TokenType type) {
      return ((List)this.tokenEndpoints.get())
         .stream()
         .filter(endpoint -> endpoint.getTokenType().equals(type))
         .findAny()
         .orElseThrow(() -> new ServiceConfigurationException(String.format("Unknown token type %s", type)));
   }
}
