package auth

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

	"bitbucket.org/ww/goautoneg"
)

const (
	requestErrAccessDenied  = "access_denied"
	requestErrInsufficient  = "insufficient"
	requestErrOverflow      = "overflow"
	requestErrInvalidValue  = "invalid_value"
	requestErrInvalidFormat = "invalid_format"
	requestErrMissing       = "missing"
	requestErrNotFound      = "not_found"
	requestErrConflict      = "conflict"
	requestErrActOfGod      = "act_of_god"
)

var (
	actOfGodResponse      = response{Errors: []requestError{requestError{Slug: requestErrActOfGod}}}
	invalidFormatResponse = response{Errors: []requestError{requestError{Slug: requestErrInvalidFormat, Field: "/"}}}

	encoders = []string{"application/json"}
)

type response struct {
	Errors   []requestError `json:"errors,omitempty"`
	Logins   []Login        `json:"logins,omitempty"`
	Profiles []Profile      `json:"profiles,omitempty"`
}

type requestError struct {
	Slug   string `json:"error,omitempty"`
	Field  string `json:"field,omitempty"`
	Param  string `json:"param,omitempty"`
	Header string `json:"header,omitempty"`
}

func negotiate(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		contentType := goautoneg.Negotiate(r.Header.Get("Accept"), encoders)
		if contentType == "" {
			w.WriteHeader(http.StatusNotAcceptable)
			log.Println("Unsupported Accept header:", r.Header.Get("Accept"))
			w.Write([]byte("Unsupported content type requested: " + r.Header.Get("Accept")))
			return
		}
		h.ServeHTTP(w, r)
	})
}

func encode(w http.ResponseWriter, r *http.Request, status int, resp response) {
	contentType := goautoneg.Negotiate(r.Header.Get("Accept"), encoders)
	w.Header().Set("content-type", contentType)
	w.WriteHeader(status)
	var err error
	switch contentType {
	case "application/json":
		enc := json.NewEncoder(w)
		err = enc.Encode(resp)
	}
	if err != nil {
		log.Println(err)
	}
}

func wrap(context Context, f func(w http.ResponseWriter, r *http.Request, context Context)) http.Handler {
	return negotiate(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		f(w, r, context)
	}))
}
