package com.vackosar.gitflowincrementalbuild.mocks.server;

import java.net.InetSocketAddress;
import java.net.URI;
import java.util.Collections;

import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.HashLoginService;
import org.eclipse.jetty.security.UserStore;
import org.eclipse.jetty.security.authentication.BasicAuthenticator;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.security.Password;
import org.eclipse.jgit.http.server.GitServlet;
import org.eclipse.jgit.lib.Repository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class HttpProtocolServer implements TestServer {

    private static final Logger LOGGER = LoggerFactory.getLogger(HttpProtocolServer.class);

    private static final String[] ROLES = new String[] { "can-access" };

    private final String username;
    private final String password;

    private Server server;

    public HttpProtocolServer() {
        this(null, null);
    }

    public HttpProtocolServer(String username, String password) {
        this.username = username;
        this.password = password;
    }

    @Override
    public URI start(Repository repo) {
        InetSocketAddress address = TestServerUtils.buildRandomLocalPortAddress();

        server = new Server(address);
        configureServer(server, repo);

        try {
            server.start();
        } catch (Exception e) {
            throw new RuntimeException("Failed to start Jetty servlet container for repo at: " + repo.getDirectory(), e);
        }
        return TestServerUtils.buildRepoUrl("http", address, server.getURI().getPort());
    }

    private void configureServer(Server server, Repository repo) {

        server.setHandler(buildServletHandler(repo));
        
        if (username != null) {
            addBasicAuth(server);
        }
    }

    private ServletHandler buildServletHandler(Repository repo) {
        GitServlet gitServlet = new GitServlet();
        gitServlet.setRepositoryResolver(new SinglePredefinedRepoResolver<>(repo));

        ServletHolder holder = new ServletHolder(gitServlet);
        ServletHandler servletHandler = new ServletHandler();
        servletHandler.addServletWithMapping(holder, "/*");
        return servletHandler;
    }

    // https://www.eclipse.org/jetty/documentation/current/configuring-security.html#_authentication_and_authorization_with_embedded_jetty
    private void addBasicAuth(Server server) {
        
        ConstraintSecurityHandler security = new ConstraintSecurityHandler();
        security.setAuthenticator(new BasicAuthenticator());

        Constraint constraint = new Constraint();
        constraint.setAuthenticate(true);
        constraint.setRoles(ROLES);
        ConstraintMapping mapping = new ConstraintMapping();
        mapping.setPathSpec("/*");
        mapping.setConstraint(constraint);
        security.setConstraintMappings(Collections.singletonList(mapping));

        HashLoginService loginService = new HashLoginService();
        loginService.setUserStore(buildUserStore());
        server.addBean(loginService);
        security.setLoginService(loginService);

        security.setHandler(server.getHandler());
        server.setHandler(security);
    }

    private UserStore buildUserStore() {
        UserStore userStore = new UserStore();
        userStore.addUser(username, new Password(password), ROLES);
        return userStore;
    }

    @Override
    public void stop() {
        try {
            server.stop();
        } catch (Exception e) {
            LOGGER.warn("Failed to stop Jetty servlet container", e);
        } finally {
            server = null;
        }
    }

}