package wfe

import (
	"fmt"
	"net/http"
	"time"

	"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/jmhodges/clock"
	"github.com/letsencrypt/boulder/core"
	blog "github.com/letsencrypt/boulder/log"
)

type requestEvent struct {
	ID           string    `json:",omitempty"`
	RealIP       string    `json:",omitempty"`
	ClientAddr   string    `json:",omitempty"`
	Endpoint     string    `json:",omitempty"`
	Method       string    `json:",omitempty"`
	RequestTime  time.Time `json:",omitempty"`
	ResponseTime time.Time `json:",omitempty"`
	Errors       []string
	Requester    int64           `json:",omitempty"`
	Contacts     []*core.AcmeURL `json:",omitempty"`

	Extra map[string]interface{} `json:",omitempty"`
}

func (e *requestEvent) AddError(msg string, args ...interface{}) {
	e.Errors = append(e.Errors, fmt.Sprintf(msg, args...))
}

type wfeHandlerFunc func(*requestEvent, http.ResponseWriter, *http.Request)

func (f wfeHandlerFunc) ServeHTTP(e *requestEvent, w http.ResponseWriter, r *http.Request) {
	f(e, w, r)
}

type wfeHandler interface {
	ServeHTTP(e *requestEvent, w http.ResponseWriter, r *http.Request)
}

type topHandler struct {
	wfe wfeHandler
	log *blog.AuditLogger
	clk clock.Clock
}

func (th *topHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	logEvent := &requestEvent{
		ID:          core.NewToken(),
		RealIP:      r.Header.Get("X-Real-IP"),
		ClientAddr:  getClientAddr(r),
		Method:      r.Method,
		RequestTime: time.Now(),
		Extra:       make(map[string]interface{}, 0),
	}
	if r.URL != nil {
		logEvent.Endpoint = r.URL.String()
	}
	defer th.logEvent(logEvent)

	th.wfe.ServeHTTP(logEvent, w, r)
}

func (th *topHandler) logEvent(logEvent *requestEvent) {
	logEvent.ResponseTime = th.clk.Now()
	var msg string
	if len(logEvent.Errors) != 0 {
		msg = "Terminated request"
	} else {
		msg = "Successful request"
	}
	th.log.InfoObject(msg, logEvent)
}

// Comma-separated list of HTTP clients involved in making this
// request, starting with the original requestor and ending with the
// remote end of our TCP connection (which is typically our own
// proxy).
func getClientAddr(r *http.Request) string {
	if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
		return xff + "," + r.RemoteAddr
	}
	return r.RemoteAddr
}
