package com.airlock.iam.common.infrastructure.event;

import com.airlock.iam.base.api.domain.model.event.DomainEvent;
import com.airlock.iam.base.api.domain.model.event.metadata.EventMetadataItem;
import com.airlock.iam.base.api.domain.model.event.metadata.StandardEventMetadataItemType;
import com.airlock.iam.common.api.domain.model.event.UserEvent;
import java.sql.Date;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

public final class CommonUserEventValuesProvider {
   public static <T extends DomainEvent> Map<String, Object> provideEventSpecificValues(
      T event, List<CommonUserEventValuesProvider.EventValuesProvider<?>> eventValuesProviders
   ) {
      return eventValuesProviders.stream()
         .filter(creator -> creator.eventType().isAssignableFrom(event.getClass()))
         .map(creator -> creator)
         .findFirst()
         .map(CommonUserEventValuesProvider.EventValuesProvider::mapCreator)
         .map(f -> f.apply(event))
         .orElseGet(Collections::emptyMap);
   }

   public static Map<String, Object> provideGenericEventValues(DomainEvent event) {
      Map<String, Object> values = new HashMap<>();
      values.put("event.id", event.id().value());
      values.put("event.createdAt", Date.from(event.createdAt()));
      if (event instanceof UserEvent userEvent) {
         values.put("event.data.userId", userEvent.userId().getValue());
      }

      for (EventMetadataItem item : event.metadata().items()) {
         if (StandardEventMetadataItemType.USER_AGENT_REQUEST_HEADER.equals(item.type())) {
            values.put("event.metadata.userAgent", item.value());
         } else if (StandardEventMetadataItemType.REQUEST_IP_ADDRESS.equals(item.type())) {
            values.put("event.metadata.requestIp", item.value());
         }
      }

      return values;
   }

   public static <T extends DomainEvent> CommonUserEventValuesProvider.EventValuesProvider<T> provider(
      Class<T> eventType, Function<T, Map<String, Object>> mapCreator
   ) {
      return new CommonUserEventValuesProvider.EventValuesProvider<>(eventType, mapCreator);
   }

   private CommonUserEventValuesProvider() {
   }

   public record EventValuesProvider<T extends DomainEvent>(Class<T> eventType, Function<T, Map<String, Object>> mapCreator) {
   }
}
