package dash

import (
	"crypto/subtle"
	"net/http"

	"github.com/gorilla/sessions"
	"github.com/seaweedfs/seaweedfs/weed/glog"
)

// ShowLogin displays the login page.
func (s *AdminServer) ShowLogin(w http.ResponseWriter, r *http.Request) {
	http.Redirect(w, r, P(r.Context(), "/login"), http.StatusSeeOther)
}

// HandleLogin handles login form submission.
func (s *AdminServer) HandleLogin(store sessions.Store, adminUser, adminPassword, readOnlyUser, readOnlyPassword string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		prefix := URLPrefixFromContext(r.Context())
		if err := r.ParseForm(); err != nil {
			http.Redirect(w, r, prefix+"/login?error=Invalid form submission", http.StatusSeeOther)
			return
		}
		session, err := store.Get(r, sessionName)
		if err != nil {
			http.Redirect(w, r, prefix+"/login?error=Unable to create session. Please try again or contact administrator.", http.StatusSeeOther)
			return
		}

		if err := ValidateSessionCSRFToken(session, r); err != nil {
			http.Redirect(w, r, prefix+"/login?error=Invalid CSRF token", http.StatusSeeOther)
			return
		}

		loginUsername := r.FormValue("username")
		loginPassword := r.FormValue("password")

		var role string
		var authenticated bool

		// Check admin credentials.
		if adminPassword != "" && loginUsername == adminUser && subtle.ConstantTimeCompare([]byte(loginPassword), []byte(adminPassword)) == 1 {
			role = "admin"
			authenticated = true
		} else if readOnlyPassword != "" && loginUsername == readOnlyUser && subtle.ConstantTimeCompare([]byte(loginPassword), []byte(readOnlyPassword)) == 1 {
			// Check read-only credentials.
			role = "readonly"
			authenticated = true
		}

		if authenticated {
			for key := range session.Values {
				delete(session.Values, key)
			}
			session.Values["authenticated"] = true
			session.Values["username"] = loginUsername
			session.Values["role"] = role
			csrfToken, err := generateCSRFToken()
			if err != nil {
				http.Redirect(w, r, prefix+"/login?error=Unable to create session. Please try again or contact administrator.", http.StatusSeeOther)
				return
			}
			session.Values[sessionCSRFTokenKey] = csrfToken
			if err := session.Save(r, w); err != nil {
				// Log the detailed error server-side for diagnostics.
				glog.Errorf("Failed to save session for user %s: %v", loginUsername, err)
				http.Redirect(w, r, prefix+"/login?error=Unable to create session. Please try again or contact administrator.", http.StatusSeeOther)
				return
			}

			http.Redirect(w, r, prefix+"/admin", http.StatusSeeOther)
			return
		}

		// Authentication failed.
		http.Redirect(w, r, prefix+"/login?error=Invalid credentials", http.StatusSeeOther)
	}
}

// HandleLogout handles user logout.
func (s *AdminServer) HandleLogout(store sessions.Store, w http.ResponseWriter, r *http.Request) {
	prefix := URLPrefixFromContext(r.Context())
	session, err := store.Get(r, sessionName)
	if err != nil {
		http.Redirect(w, r, prefix+"/login", http.StatusSeeOther)
		return
	}
	for key := range session.Values {
		delete(session.Values, key)
	}
	session.Options.MaxAge = -1
	if err := session.Save(r, w); err != nil {
		glog.Warningf("Failed to save session during logout: %v", err)
	}
	http.Redirect(w, r, prefix+"/login", http.StatusSeeOther)
}
