package auth

import (
	"context"
	"errors"
	"net/http"

	"github.com/kirsle/blog/core/internal/models/users"
	"github.com/kirsle/blog/core/internal/sessions"
	"github.com/kirsle/blog/core/internal/types"
	"github.com/urfave/negroni"
)

// CurrentUser returns the current user's object.
func CurrentUser(r *http.Request) (*users.User, error) {
	session := sessions.Get(r)
	if loggedIn, ok := session.Values["logged-in"].(bool); ok && loggedIn {
		id := session.Values["user-id"].(int)
		u, err := users.LoadReadonly(id)
		u.IsAuthenticated = true
		return u, err
	}

	return &users.User{
		Admin: false,
	}, errors.New("not authenticated")
}

// Login logs the browser in as the given user.
func Login(w http.ResponseWriter, r *http.Request, u *users.User) error {
	session, err := sessions.Store.Get(r, "session") // TODO session name
	if err != nil {
		return err
	}
	session.Values["logged-in"] = true
	session.Values["user-id"] = u.ID
	session.Save(r, w)
	return nil
}

// LoggedIn returns whether the current user is logged in to an account.
func LoggedIn(r *http.Request) bool {
	session := sessions.Get(r)
	if loggedIn, ok := session.Values["logged-in"].(bool); ok && loggedIn {
		return true
	}
	return false
}

// LoginRequired is a middleware that requires a logged-in user.
func LoginRequired(onError http.HandlerFunc) negroni.HandlerFunc {
	middleware := func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
		ctx := r.Context()
		if user, ok := ctx.Value(types.UserKey).(*users.User); ok {
			if user.ID > 0 {
				next(w, r)
				return
			}
		}
		onError(w, r)
	}

	return middleware
}

// Middleware loads the user's authentication state from their session cookie.
func Middleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	u, err := CurrentUser(r)
	if err != nil {
		next(w, r)
		return
	}

	ctx := context.WithValue(r.Context(), types.UserKey, u)
	next(w, r.WithContext(ctx))
}
