// Copyright (C) The Arvados Authors. All rights reserved.
//
// SPDX-License-Identifier: AGPL-3.0

package githttpd

import (
	"context"
	"net"
	"net/http"
	"net/http/cgi"
	"os"

	"git.arvados.org/arvados.git/sdk/go/arvados"
	"git.arvados.org/arvados.git/sdk/go/ctxlog"
)

// gitHandler is an http.Handler that invokes git-http-backend (or
// whatever backend is configured) via CGI, with appropriate
// environment variables in place for git-http-backend or
// gitolite-shell.
type gitHandler struct {
	cgi.Handler
}

func newGitHandler(ctx context.Context, cluster *arvados.Cluster) http.Handler {
	const glBypass = "GL_BYPASS_ACCESS_CHECKS"
	const glHome = "GITOLITE_HTTP_HOME"
	var env []string
	path := os.Getenv("PATH")
	if cluster.Git.GitoliteHome != "" {
		env = append(env,
			glHome+"="+cluster.Git.GitoliteHome,
			glBypass+"=1")
		path = path + ":" + cluster.Git.GitoliteHome + "/bin"
	} else if home, bypass := os.Getenv(glHome), os.Getenv(glBypass); home != "" || bypass != "" {
		env = append(env, glHome+"="+home, glBypass+"="+bypass)
		ctxlog.FromContext(ctx).Printf("DEPRECATED: Passing through %s and %s environment variables. Use GitoliteHome configuration instead.", glHome, glBypass)
	}

	var listen arvados.URL
	for listen = range cluster.Services.GitHTTP.InternalURLs {
		break
	}
	env = append(env,
		"GIT_PROJECT_ROOT="+cluster.Git.Repositories,
		"GIT_HTTP_EXPORT_ALL=",
		"SERVER_ADDR="+listen.Host,
		"PATH="+path)
	return &gitHandler{
		Handler: cgi.Handler{
			Path: cluster.Git.GitCommand,
			Dir:  cluster.Git.Repositories,
			Env:  env,
			Args: []string{"http-backend"},
		},
	}
}

func (h *gitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	remoteHost, remotePort, err := net.SplitHostPort(r.RemoteAddr)
	if err != nil {
		ctxlog.FromContext(r.Context()).Errorf("Internal error: SplitHostPort(r.RemoteAddr==%q): %s", r.RemoteAddr, err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	// Copy the wrapped cgi.Handler, so these request-specific
	// variables don't leak into the next request.
	handlerCopy := h.Handler
	handlerCopy.Env = append(handlerCopy.Env,
		// In Go1.5 we can skip this, net/http/cgi will do it for us:
		"REMOTE_HOST="+remoteHost,
		"REMOTE_ADDR="+remoteHost,
		"REMOTE_PORT="+remotePort,
		// Ideally this would be a real username:
		"REMOTE_USER="+r.RemoteAddr,
	)
	handlerCopy.ServeHTTP(w, r)
}