package admin

import (
	"context"
	"net/http"
)

type ctxKey string

const ctxKeySession ctxKey = "admin_session"

// SessionMiddleware loads and validates the session cookie for protected
// routes. Requests without a valid session are redirected to the login page
// (GET) or rejected with 401 (other methods). On success the session is stored
// in the request context and its idle TTL is slid forward.
type SessionMiddleware struct {
	sessions *SessionStore
	next     http.Handler
}

// NewSessionMiddleware wraps next so it only runs with a valid session.
func NewSessionMiddleware(sessions *SessionStore, next http.Handler) *SessionMiddleware {
	return &SessionMiddleware{sessions: sessions, next: next}
}

func (m *SessionMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	c, err := r.Cookie(SessionCookieName)
	if err != nil {
		m.deny(w, r)
		return
	}
	sess, serr := m.sessions.Get(r.Context(), c.Value)
	if serr != nil {
		http.Error(w, "internal error", http.StatusInternalServerError)
		return
	}
	if sess == nil {
		m.deny(w, r)
		return
	}
	ctx := context.WithValue(r.Context(), ctxKeySession, sess)
	m.next.ServeHTTP(w, r.WithContext(ctx))
}

func (m *SessionMiddleware) deny(w http.ResponseWriter, r *http.Request) {
	if r.Method == http.MethodGet {
		http.Redirect(w, r, "/login", http.StatusFound)
		return
	}
	http.Error(w, "unauthorized", http.StatusUnauthorized)
}

// SessionFromContext returns the authenticated session, or nil.
func SessionFromContext(ctx context.Context) *Session {
	s, _ := ctx.Value(ctxKeySession).(*Session)
	return s
}
