package api

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"code.justin.tv/chat/timing"
	"code.justin.tv/common/chitin"
	"code.justin.tv/video/spectre/util"
	"github.com/zenazn/goji/web"
	"github.com/zenazn/goji/web/mutil"
)

type statsdMiddleware struct {
	c *web.C
	h http.Handler
}

func (sm statsdMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	match := web.GetMatch(*sm.c)
	route := fmt.Sprintf("%v", match.RawPattern())

	// Build a proxy response writer that will record the status. That way
	// we can use it when filing the statsd data.
	newWriter := mutil.WrapWriter(w)

	graphiteName := strings.TrimLeft(route, "/")
	if graphiteName != "" {
		xact := timing.Xact{
			Name:  strings.Join([]string{"endpoint", graphiteName}, "."),
			Stats: router.StatsdClient,
		}
		xact.Start()

		// This has to be wrapped in an anonymous function otherwise
		// newWriter.statusCode will be evaluated when the defer is
		// instantiated and not when final returns.
		defer func() { xact.End(strconv.Itoa(newWriter.Status())) }()
	}

	sm.h.ServeHTTP(newWriter, r)
}

func JSONHeaderMiddleware(c *web.C, h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func StatsdMiddleware(c *web.C, h http.Handler) http.Handler {
	return statsdMiddleware{c, h}
}

func RecoverPanics(c *web.C, h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		match := web.GetMatch(*c)
		route := fmt.Sprintf("%v", match.RawPattern())

		// Capture panics, make sure we return a status code. In addition this
		// will cause them to be logged to rollbar.
		defer func() {
			if p := recover(); p != nil {
				util.JSONError(w,
					http.StatusInternalServerError,
					fmt.Sprintf("panic in %q", route),
					fmt.Errorf("Panic: %v", p), // Probably a better way to format this.
				)
				panic(p)
			}
		}()

		h.ServeHTTP(w, r)
	}

	return http.HandlerFunc(fn)
}

func ChitinMiddleware(h http.Handler) http.Handler {
	return chitin.Handler(h, chitin.SetBaseContext(util.MustTraceContext()))
}
