package com.telephoners.krakyournet.ctf;

import com.bazaarvoice.dropwizard.webjars.WebJarBundle;
import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.ClassPath;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.telephoners.krakyournet.ctf.auth.UserAuthenticator;
import com.telephoners.krakyournet.ctf.beans.User;
import com.telephoners.krakyournet.ctf.commands.PurgeDatabaseCommand;
import com.telephoners.krakyournet.ctf.commands.RegisterTasksCommand;
import com.telephoners.krakyournet.ctf.commands.RegisterTeamsCommand;
import com.telephoners.krakyournet.ctf.core.ApplicationConfiguration;
import com.telephoners.krakyournet.ctf.logging.LoggingFilter;
import com.telephoners.krakyournet.ctf.modules.ApplicationModule;
import com.telephoners.krakyournet.ctf.repositories.UsersRepository;
import io.dropwizard.Application;
import io.dropwizard.assets.AssetsBundle;
import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
import io.dropwizard.jersey.setup.JerseyEnvironment;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;

import java.io.IOException;
import java.util.stream.Stream;

public class CTFApplication extends Application<ApplicationConfiguration>
{
    private Injector injector;

    @Override
    public void initialize(final Bootstrap<ApplicationConfiguration> bootstrap)
    {
        bootstrap.addBundle(new AssetsBundle("/assets_build", "/", "index.html"));
        bootstrap.addCommand(new PurgeDatabaseCommand());
        bootstrap.addCommand(new RegisterTasksCommand());
        bootstrap.addCommand(new RegisterTeamsCommand());
        Stream.of("org.webjars.npm", "org.webjars.bower")
                .map(WebJarBundle::new)
                .forEach(bootstrap::addBundle);
    }

    private void registerResources(Environment environment) throws IOException
    {
        JerseyEnvironment jersey = environment.jersey();
        final ClassPath classPath = ClassPath.from(this.getClass().getClassLoader());
        ImmutableSet<ClassPath.ClassInfo> resourceClasses = classPath.getTopLevelClasses("com.telephoners.krakyournet.ctf.resources");
        resourceClasses.stream()
                .forEach(classInfo -> jersey.register(injector.getInstance(classInfo.load())));
    }

    private void registerAuthFeatures(Environment environment)
    {
        environment.jersey().register(new AuthDynamicFeature(
                new BasicCredentialAuthFilter.Builder<User>()
                        .setAuthenticator(injector.getInstance(UserAuthenticator.class))
                        .buildAuthFilter()));
        environment.jersey().register(RolesAllowedDynamicFeature.class);
        environment.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
    }

    private void registerFilters(Environment environment){
        environment.jersey().register(new LoggingFilter());
    }

    @Override
    public void run(ApplicationConfiguration applicationConfiguration, Environment environment) throws Exception
    {
        injector = createInjector(applicationConfiguration);

        registerAdmins(applicationConfiguration);
        registerResources(environment);
        registerAuthFeatures(environment);
        registerFilters(environment);
    }

    private void registerAdmins(ApplicationConfiguration applicationConfiguration)
    {
        UsersRepository usersRepository = injector.getInstance(UsersRepository.class);
        applicationConfiguration.getAdmins().forEach(usersRepository::add);
    }

    private Injector createInjector(ApplicationConfiguration applicationConfiguration)
    {
        return Guice.createInjector(new ApplicationModule(applicationConfiguration));
    }

    public static void main(String[] args) throws Exception
    {
        new CTFApplication().run(args);
    }
}
