package session

import (
	"context"
	"fmt"
	"net/http"
	"time"

	"git.kirsle.net/apps/gophertype/pkg/console"
	"github.com/gorilla/sessions"
)

// Store holds your cookie store information.
var Store sessions.Store

// SetSecretKey initializes a session cookie store with the secret key.
func SetSecretKey(keyPairs ...[]byte) {
	Store = sessions.NewCookieStore(keyPairs...)
}

// Middleware gets the Gorilla session store and makes it available on the
// Request context.
//
// Middleware is the first custom middleware applied, so it takes the current
// datetime to make available later in the request and stores it on the request
// context.
func Middleware(next http.Handler) http.Handler {
	middleware := func(w http.ResponseWriter, r *http.Request) {
		// Set the HTML content-type header by default until overridden by a handler.
		w.Header().Set("Content-Type", "text/html; charset=utf-8")

		// Store the current datetime on the request context.
		ctx := context.WithValue(r.Context(), StartTimeKey, time.Now())

		// Get the Gorilla session and make it available in the request context.
		session, _ := Store.Get(r, "session")
		ctx = context.WithValue(ctx, SessionKey, session)

		next.ServeHTTP(w, r.WithContext(ctx))
	}

	return http.HandlerFunc(middleware)
}

// Get returns the current request's session.
func Get(r *http.Request) *sessions.Session {
	if r == nil {
		panic("Session(*http.Request) with a nil argument!?")
	}

	ctx := r.Context()
	if session, ok := ctx.Value(SessionKey).(*sessions.Session); ok {
		return session
	}

	// If the session wasn't on the request, it means I broke something.
	console.Warn(
		"Session(): didn't find session in request context! Getting it " +
			"from the session store instead.",
	)
	session, _ := Store.Get(r, "session")
	return session
}

// Flash adds a flashed message to the session for the next template rendering.
func Flash(w http.ResponseWriter, r *http.Request, msg string, args ...interface{}) {
	sess := Get(r)

	var flashes []string
	if v, ok := sess.Values["flashes"].([]string); ok {
		flashes = v
	}

	flashes = append(flashes, fmt.Sprintf(msg, args...))
	sess.Values["flashes"] = flashes
	sess.Save(r, w)
}

// GetFlashes returns all the flashes from the session and clears the queue.
func GetFlashes(w http.ResponseWriter, r *http.Request) []string {
	sess := Get(r)
	if flashes, ok := sess.Values["flashes"].([]string); ok {
		sess.Values["flashes"] = []string{}
		sess.Save(r, w)
		return flashes
	}
	return []string{}
}
