package http

import (
	"context"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"

	"github.com/davidlick/bookish/bookish-server/internal"
	"github.com/davidlick/bookish/bookish-server/mysql"
	"github.com/go-chi/chi"
)

// cors is a middlware that adds CORS headers to responses.
func cors(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS")
		w.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, Authorization")
		w.Header().Set("Access-Control-Allow-Credentials", "true")
		w.Header().Set("Access-Control-Max-Age", "600")

		if r.Method == "OPTIONS" {
			return
		}

		next.ServeHTTP(w, r)
	})
}

// renterCtx is a convenience handler to fetch the renter with the provided renterId URLParam.
func (s *Server) renterCtx(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		id := chi.URLParam(r, "renterId")

		renter, err := s.Renters.FetchRenter(id)
		if err != nil && errors.Is(mysql.ErrNotFound, err) {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
			return
		} else if err != nil {
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
			return
		}

		ctx = context.WithValue(ctx, internal.CtxRenter, renter)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

// bookInLibrary is a convenience handler that queries the books API for a map of books available
// at the library.
func (s *Server) bookInLibrary(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		s.UpdateMap()

		reqBytes, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
			return
		}

		var req struct {
			Title string `json:"title"`
		}

		err = json.Unmarshal(reqBytes, &req)
		if err != nil {
			http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
			return
		}

		if _, found := s.LibraryMap.Map[req.Title]; !found {
			http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
			return
		}

		ctx = context.WithValue(ctx, internal.CtxBookTitle, req.Title)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}
