package main

import (
	"net/http"
	"time"

	"github.com/gorilla/sessions"
	"github.com/pquerna/otp/totp"
	"golang.org/x/crypto/bcrypt"
)

var (
	store            = sessions.NewCookieStore([]byte("super-secret-key")) // Change this in production
	appStartupTime   time.Time
)

func initAuth() {
	appStartupTime = time.Now()
}

func authMiddleware(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		session, _ := store.Get(r, "session")
		if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
			http.Redirect(w, r, "/login", http.StatusSeeOther)
			return
		}

		// Validate session version - invalidate if app restarted or password changed
		if sessionVersion, ok := session.Values["version"].(string); !ok || sessionVersion != getSessionVersion() {
			session.Values["authenticated"] = false
			session.Save(r, w)
			http.Redirect(w, r, "/login", http.StatusSeeOther)
			return
		}

		// Check session startup time - invalidate if app was restarted
		if sessionStartupTime, ok := session.Values["startup_time"].(int64); !ok || sessionStartupTime != appStartupTime.Unix() {
			session.Values["authenticated"] = false
			session.Save(r, w)
			http.Redirect(w, r, "/login", http.StatusSeeOther)
			return
		}

		// Check session expiration
		if sessionCreatedAt, ok := session.Values["created_at"].(int64); ok {
			configMu.RLock()
			timeoutMins := config.SessionTimeoutMins
			configMu.RUnlock()

			if time.Now().Unix()-sessionCreatedAt > int64(timeoutMins*60) {
				session.Values["authenticated"] = false
				session.Save(r, w)
				http.Redirect(w, r, "/login", http.StatusSeeOther)
				return
			}
		}

		next(w, r)
	}
}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		username := r.FormValue("username")
		password := r.FormValue("password")
		mfaCode := r.FormValue("mfa_code")

		configMu.RLock()
		defer configMu.RUnlock()

		if username != config.Username || bcrypt.CompareHashAndPassword([]byte(config.HashedPass), []byte(password)) != nil {
			http.Error(w, "Invalid credentials", http.StatusUnauthorized)
			return
		}

		if config.MFASecret != "" {
			valid := totp.Validate(mfaCode, config.MFASecret)
			if !valid {
				http.Error(w, "Invalid MFA code", http.StatusUnauthorized)
				return
			}
		}

		session, _ := store.Get(r, "session")
		session.Values["authenticated"] = true
		session.Values["version"] = getSessionVersion()
		session.Values["startup_time"] = appStartupTime.Unix()
		session.Values["created_at"] = time.Now().Unix()
		session.Save(r, w)

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

	renderTemplate(w, r, "login.html", nil)
}

func logoutHandler(w http.ResponseWriter, r *http.Request) {
	session, _ := store.Get(r, "session")
	session.Values["authenticated"] = false
	session.Values["version"] = ""
	session.Save(r, w)
	http.Redirect(w, r, "/login", http.StatusSeeOther)
}

func getSessionVersion() string {
	configMu.RLock()
	defer configMu.RUnlock()
	// Create a version string from password hash - changes when password changes
	return config.HashedPass
}
