package api

import (
	"net/http"

	"code.secondbit.org/api.hg"
	"code.secondbit.org/ducky/subscriptions.hg"
	"code.secondbit.org/trout.hg"
	"code.secondbit.org/uuid.hg"

	"golang.org/x/net/context"
)

const (
	SubscriptionScope      = "subscriptions"
	SubscriptionAdminScope = "subscriptions_admin"
)

func HandleSubscriptions(router *trout.Router, c context.Context) {
	router.Endpoint("/subscriptions").Methods("POST", "OPTIONS").Handler(
		api.CORSMiddleware(api.NegotiateMiddleware(api.ContextWrapper(c, CreateSubscriptionHandler))))
	router.Endpoint("/subscriptions/{id}").Methods("GET", "OPTIONS").Handler(
		api.CORSMiddleware(api.NegotiateMiddleware(api.ContextWrapper(c, GetSubscriptionHandler))))
}

func CreateSubscriptionHandler(w http.ResponseWriter, r *http.Request, c context.Context) {
	store, err := getSubscriptionStore(c)
	if err != nil {
		api.Encode(w, r, http.StatusInternalServerError, Response{Errors: api.ActOfGodError})
		return
	}
	stripe, err := getStripeClient(c)
	if err != nil {
		api.Encode(w, r, http.StatusInternalServerError, Response{Errors: api.ActOfGodError})
		return
	}
	if !api.CheckScopes(r, SubscriptionScope) {
		api.Encode(w, r, http.StatusUnauthorized, Response{Errors: []api.RequestError{{Slug: api.RequestErrAccessDenied}}})
		return
	}
	userID, err := api.AuthUser(r)
	if err != nil {
		api.Encode(w, r, http.StatusUnauthorized, Response{Errors: []api.RequestError{{Slug: api.RequestErrAccessDenied}}})
		return
	}
	var req subscriptions.SubscriptionRequest
	err = api.Decode(r, &req)
	if err != nil {
		api.Encode(w, r, http.StatusBadRequest, Response{Errors: api.InvalidFormatError})
		return
	}
	errs := req.Validate(userID, api.CheckScopes(r, SubscriptionAdminScope))
	if len(errs) != 0 {
		api.Encode(w, r, http.StatusBadRequest, Response{Errors: errs})
		return
	}
	// TODO: need some way of validating the email they sent actually belongs to them
	sub, err := subscriptions.New(req, stripe, store)
	if err != nil {
		var rErr api.RequestError
		var code int
		switch err {
		case subscriptions.ErrSubscriptionAlreadyExists:
			rErr = api.RequestError{Slug: api.RequestErrConflict, Field: "/user_id"}
			code = http.StatusBadRequest
		case subscriptions.ErrStripeSubscriptionAlreadyExists:
			rErr = api.RequestError{Slug: api.RequestErrConflict, Field: "/stripe_token"}
			code = http.StatusBadRequest
		default:
			rErr = api.RequestError{Slug: api.RequestErrActOfGod}
			code = http.StatusInternalServerError
		}
		api.Encode(w, r, code, Response{Errors: []api.RequestError{rErr}})
		return
	}
	resp := Response{Subscriptions: []subscriptions.Subscription{sub}}
	api.Encode(w, r, http.StatusCreated, resp)
}

func GetSubscriptionHandler(w http.ResponseWriter, r *http.Request, c context.Context) {
	store, err := getSubscriptionStore(c)
	if err != nil {
		api.Encode(w, r, http.StatusInternalServerError, Response{Errors: api.ActOfGodError})
		return
	}
	vars := trout.RequestVars(r)
	rawID := vars.Get("id")
	if rawID == "" {
		api.Encode(w, r, http.StatusBadRequest, Response{Errors: []api.RequestError{{Slug: api.RequestErrMissing, Param: "id"}}})
		return
	}
	id, err := uuid.Parse(rawID)
	if err != nil {
		api.Encode(w, r, http.StatusBadRequest, Response{Errors: []api.RequestError{{Slug: api.RequestErrInvalidFormat, Param: "id"}}})
		return
	}
	if !api.CheckScopes(r, SubscriptionScope) {
		api.Encode(w, r, http.StatusUnauthorized, Response{Errors: []api.RequestError{{Slug: api.RequestErrAccessDenied}}})
		return
	}
	userID, err := api.AuthUser(r)
	if err != nil {
		api.Encode(w, r, http.StatusUnauthorized, Response{Errors: []api.RequestError{{Slug: api.RequestErrAccessDenied}}})
		return
	}
	if !id.Equal(userID) && !api.CheckScopes(r, SubscriptionAdminScope) {
		api.Encode(w, r, http.StatusUnauthorized, Response{Errors: []api.RequestError{{Slug: api.RequestErrAccessDenied}}})
		return
	}
	subs, err := store.GetSubscriptions([]uuid.ID{id})
	if err != nil {
		api.Encode(w, r, http.StatusInternalServerError, Response{Errors: api.ActOfGodError})
		return
	}
	sub, ok := subs[id.String()]
	if !ok {
		api.Encode(w, r, http.StatusNotFound, Response{Errors: []api.RequestError{{Slug: api.RequestErrNotFound, Param: "id"}}})
		return
	}
	resp := Response{Subscriptions: []subscriptions.Subscription{sub}}
	api.Encode(w, r, http.StatusOK, resp)
}
