package com.airlock.iam.common.infrastructure;

import com.airlock.iam.common.api.infrastructure.mapper.MapperConfiguration;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.ws.rs.core.Configurable;
import java.util.Collection;
import java.util.function.Consumer;
import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.ConfigurableMapper;
import org.glassfish.hk2.utilities.binding.AbstractBinder;

public class OrikaMapperConfigurator {
   public void registerOrikaMappings(Configurable<?> configurableApplication, RestApplicationConfig config) {
      ConfigurableMapper mapper = this.getOrikaMapper(config);
      configurableApplication.register(new OrikaMapperConfigurator.MapperBinder(mapper));
   }

   public ConfigurableMapper getOrikaMapper(RestApplicationConfig config) {
      return new OrikaMapperConfigurator.BeansMapper(config.applicationComponentProvider().beanMappers());
   }

   private static final class BeansMapper extends ConfigurableMapper {
      private final Collection<Consumer<MapperConfiguration>> beanMappers;

      public BeansMapper(Collection<Consumer<MapperConfiguration>> beanMappers) {
         super(false);
         this.beanMappers = beanMappers;
         this.init();
      }

      protected void configure(MapperFactory factory) {
         this.beanMappers.forEach(c -> c.accept(new MapperConfiguration(factory)));
      }
   }

   private static final class MapperBinder extends AbstractBinder {
      private final ConfigurableMapper mapper;

      protected void configure() {
         this.bind(this.mapper).to(MapperFacade.class);
      }

      @SuppressFBWarnings(justification = "generated code")
      public MapperBinder(ConfigurableMapper mapper) {
         this.mapper = mapper;
      }
   }
}
