package com.telephoners.krakyournet.ctf;

import com.bazaarvoice.dropwizard.webjars.WebJarBundle;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.telephoners.krakyournet.ctf.auth.ExampleAuthenticator;
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.modules.ApplicationModule;
import com.telephoners.krakyournet.ctf.objects.User;
import com.telephoners.krakyournet.ctf.repositories.UsersRepository;
import com.telephoners.krakyournet.ctf.resources.*;
import io.dropwizard.Application;
import io.dropwizard.assets.AssetsBundle;
import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.Authorizer;
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.util.stream.Stream;

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

    @Override
    public void initialize(final Bootstrap<ApplicationConfiguration> bootstrap)
    {
        //todo: refactor, will not work on artifact?
        bootstrap.addBundle(new AssetsBundle("/assets_build", "/page", "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)
    {
        //todo: use reflections to iterate over resources
        JerseyEnvironment jersey = environment.jersey();
        jersey.register(injector.getInstance(TeamsResource.class));
        jersey.register(injector.getInstance(TasksResource.class));
        jersey.register(injector.getInstance(ProxyResource.class));
        jersey.register(injector.getInstance(SolutionsResource.class));
        jersey.register(injector.getInstance(TaskResource.class));
        jersey.register(injector.getInstance(UtilResource.class));
    }

    private void registerAuthFeatures(Environment environment)
    {
        environment.jersey().register(new AuthDynamicFeature(
                new BasicCredentialAuthFilter.Builder<User>()
                        .setAuthenticator(injector.getInstance(ExampleAuthenticator.class))
                        .setAuthorizer(new ExampleAuthorizer())
                        .setRealm("SUPER SECRET STUFF")
                        //.setPrefix("Not-So-Basic")
                        .buildAuthFilter()));
        environment.jersey().register(RolesAllowedDynamicFeature.class);
        environment.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
    }

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

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

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

    //todo: remove
    public class ExampleAuthorizer implements Authorizer<User>
    {
        @Override
        public boolean authorize(User user, String role)
        {
            return user.getName().equals("good-guy") && role.equals("ADMIN");
        }
    }

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

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