package server

import (
	"encoding/json"
	"net/http"

	context "github.com/docker/distribution/context"

	"k8s.io/kubernetes/pkg/client/restclient"

	"github.com/openshift/origin/pkg/client"
)

type tokenHandler struct {
	ctx             context.Context
	anonymousConfig restclient.Config
}

// NewTokenHandler returns a handler that implements the docker token protocol
func NewTokenHandler(ctx context.Context, client RegistryClient) http.Handler {
	return &tokenHandler{
		ctx:             ctx,
		anonymousConfig: client.SafeClientConfig(),
	}
}

// bearer token issued to token requests that present no credentials
// recognized by the openshift auth provider as identifying the anonymous user
const anonymousToken = "anonymous"

func (t *tokenHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	ctx := context.WithRequest(t.ctx, req)

	// If no authorization is provided, return a token the auth provider will treat as an anonymous user
	if len(req.Header.Get("Authorization")) == 0 {
		context.GetRequestLogger(ctx).Debugf("anonymous token request")
		t.writeToken(anonymousToken, w, req)
		return
	}

	// use the password as the token
	_, token, ok := req.BasicAuth()
	if !ok {
		context.GetRequestLogger(ctx).Debugf("no basic auth credentials provided")
		t.writeUnauthorized(w, req)
		return
	}

	// TODO: if this doesn't validate as an API token, attempt to obtain an API token using the given username/password
	copied := t.anonymousConfig
	copied.BearerToken = token
	osClient, err := client.New(&copied)
	if err != nil {
		context.GetRequestLogger(ctx).Errorf("error building client: %v", err)
		t.writeError(w, req)
		return
	}

	if _, err := osClient.Users().Get("~"); err != nil {
		context.GetRequestLogger(ctx).Debugf("invalid token: %v", err)
		t.writeUnauthorized(w, req)
		return
	}

	t.writeToken(token, w, req)
}

func (t *tokenHandler) writeError(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(400)
	json.NewEncoder(w).Encode(map[string]interface{}{"error": "invalid_request"})
}

func (t *tokenHandler) writeToken(token string, w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(200)
	json.NewEncoder(w).Encode(map[string]interface{}{
		"token":        token,
		"access_token": token,
	})
}

func (t *tokenHandler) writeUnauthorized(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(401)
}
