package middleware

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

	"git.kirsle.net/apps/gosocial/pkg/config"
	"git.kirsle.net/apps/gosocial/pkg/controller/photo"
	"git.kirsle.net/apps/gosocial/pkg/log"
	"git.kirsle.net/apps/gosocial/pkg/models"
	"git.kirsle.net/apps/gosocial/pkg/session"
	"git.kirsle.net/apps/gosocial/pkg/templates"
)

// LoginRequired middleware.
func LoginRequired(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		// User must be logged in.
		user, err := session.CurrentUser(r)
		if err != nil {
			log.Error("LoginRequired: %s", err)
			session.FlashError(w, r, "You must be signed in to view this page.")
			templates.Redirect(w, "/login?next="+r.URL.RawPath)
			return
		}

		// Are they banned or disabled?
		if user.Status == models.UserStatusDisabled {
			session.LogoutUser(w, r)
			session.FlashError(w, r, "Your account has been disabled and you are now logged out.")
			templates.Redirect(w, "/")
			return
		} else if user.Status == models.UserStatusBanned {
			session.LogoutUser(w, r)
			session.FlashError(w, r, "Your account has been banned and you are now logged out.")
			templates.Redirect(w, "/")
			return
		}

		// Ping LastLoginAt for long lived sessions.
		if time.Since(user.LastLoginAt) > config.LastLoginAtCooldown {
			user.LastLoginAt = time.Now()
			if err := user.Save(); err != nil {
				log.Error("LoginRequired: couldn't refresh LastLoginAt for user %s: %s", user.Username, err)
			}
		}

		// Stick the CurrentUser in the request context so future calls to session.CurrentUser can read it.
		ctx := context.WithValue(r.Context(), session.CurrentUserKey, user)
		handler.ServeHTTP(w, r.WithContext(ctx))
	})
}

// AdminRequired middleware.
func AdminRequired(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		// User must be logged in.
		currentUser, err := session.CurrentUser(r)
		if err != nil {
			log.Error("AdminRequired: %s", err)
			errhandler := templates.MakeErrorPage("Login Required", "You must be signed in to view this page.", http.StatusForbidden)
			errhandler.ServeHTTP(w, r)
			return
		}

		// Stick the CurrentUser in the request context so future calls to session.CurrentUser can read it.
		ctx := context.WithValue(r.Context(), session.CurrentUserKey, currentUser)

		// Admin required.
		if !currentUser.IsAdmin {
			log.Error("AdminRequired: %s", err)
			errhandler := templates.MakeErrorPage("Admin Required", "You do not have permission for this page.", http.StatusForbidden)
			errhandler.ServeHTTP(w, r.WithContext(ctx))
			return
		}

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

// CertRequired middleware: like LoginRequired but user must also have their verification pic certified.
func CertRequired(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		// User must be logged in.
		currentUser, err := session.CurrentUser(r)
		if err != nil {
			log.Error("LoginRequired: %s", err)
			session.FlashError(w, r, "You must be signed in to view this page.")
			templates.Redirect(w, "/login?next="+r.URL.Path)
			return
		}

		// User must be certified.
		if !currentUser.Certified || currentUser.ProfilePhoto.ID == 0 {
			log.Error("CertRequired: user is not certified")
			photo.CertificationRequiredError().ServeHTTP(w, r)
			return
		}

		handler.ServeHTTP(w, r)
	})
}
