import auth.ExampleAuthenticator;
import com.bazaarvoice.dropwizard.webjars.WebJarBundle;
import com.fasterxml.jackson.dataformat.yaml.snakeyaml.Yaml;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.mongodb.MongoClient;
import commands.PurgeDatabaseCommand;
import core.ApplicationConfiguration;
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 objects.User;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.Morphia;
import repositories.UsersRepository;
import resources.*;

import java.util.List;
import java.util.stream.Stream;

/**
 * Created by gpietrus on 16.02.16.
 */
public class CTFApplication extends Application<ApplicationConfiguration>
{

    private Datastore datastore;
    private Injector injector;

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

    private void registerResources(Environment environment)
    {
        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));
    }

    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")
                        .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 AbstractModule()
        {
            @Override
            protected void configure()
            {
                bind(ApplicationConfiguration.class).toInstance(applicationConfiguration); //todo: refactor

                Morphia morphia = new Morphia();
                morphia.mapPackage("objects.Team"); //todo? what for?
                datastore = morphia.createDatastore(
                        new MongoClient(applicationConfiguration.getDbHost(),applicationConfiguration.getDbPort()), applicationConfiguration.getDbName());
                datastore.ensureIndexes();
                bind(Datastore.class).toInstance(datastore);
            }
        });
    }

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