package rest

import (
	"net/http"
	"context"
	"net/url"
	"path"
)

type contextKey struct {
	name string
}

// StatusCtxKey is a context key to record a future HTTP response status code.
var StatusCtxKey = &contextKey{"Status"}

// Status sets a HTTP response status code hint into request context at any
// point during the request life-cycle. Before the Responder sends its response
// header it will check the StatusCtxKey
func Status(r *http.Request, status int) {
	*r = *r.WithContext(context.WithValue(r.Context(), StatusCtxKey, status))
}

// Location ensures a given HTTP Location header is either an absolute reference
// (has a non-empty schema) or relative reference (has no schema nor authority,
// but an absolute path).
func Location(reference string) string {
	u, err := url.Parse(reference)
	if err != nil {
		panic("rest: invalid location: " + err.Error())
	}

	if u.IsAbs() {
		return u.String()
	}

	// Make sure the relative reference is an "absolute-path reference".
	u.User = nil
	u.Host = ""
	if u.Path != "" && !path.IsAbs(u.Path) {
		u.Path = "/" + u.Path
	}

	return u.String()
}

// NoContent returns a HTTP 204 "No Content" response.
func NoContent(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusNoContent)
}

// Conflict returns a HTTP 409 "Conflict" response.
func Conflict(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusConflict)
}

// Created returns a HTTP 201 "Created" response.
//
// It differs from `http.Redirect(w, r, http.StatusCreated, loc)` in that
// headers are not stripped.
func Created(w http.ResponseWriter, _ *http.Request, loc string) {
	w.Header().Set("Location", Location(loc))
	w.WriteHeader(http.StatusCreated)
}

// CreatedJSON returns a HTTP 201 "Created" response.
//
// It differs from `Created` in that it also writes a JSON body.
func CreatedJSON(w http.ResponseWriter, r *http.Request, loc string, v interface{}) {
	w.Header().Set("Location", Location(loc))
	Status(r, http.StatusCreated)
	JSON(w, r, v)
}
