package api

import (
	"encoding/json"
	"net/http"
	"time"

	"archivmail/internal/audit"
)

const (
	loginMaxFailures = 5
	loginWindow      = 15 * time.Minute
)

func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Username string `json:"username"`
		Password string `json:"password"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "invalid request body")
		return
	}

	// Rate-limiting: block after too many recent failures
	failures, err := s.users.CountRecentFailures(req.Username, loginWindow)
	if err == nil && failures >= loginMaxFailures {
		s.audlog.Log(audit.Entry{
			EventType: audit.EventLogin,
			Username:  req.Username,
			IPAddress: s.remoteIP(r),
			Success:   false,
			Detail:    "rate limited",
		})
		writeError(w, http.StatusTooManyRequests, "too many failed login attempts, try again later")
		return
	}

	token, user, totpRequired, err := s.authMgr.Login(req.Username, req.Password)
	if err != nil {
		_ = s.users.RecordLoginAttempt(req.Username, s.remoteIP(r))
		s.audlog.Log(audit.Entry{
			EventType: audit.EventLogin,
			Username:  req.Username,
			IPAddress: s.remoteIP(r),
			Success:   false,
			Detail:    classifyLoginError(err),
		})
		writeError(w, http.StatusUnauthorized, "invalid credentials")
		return
	}

	// PROJ-24: If TOTP is enabled, return a pending token instead of a full session.
	if totpRequired {
		s.audlog.Log(audit.Entry{
			EventType: audit.EventLogin,
			Username:  user.Username,
			TenantID:  user.TenantID,
			IPAddress: s.remoteIP(r),
			Success:   true,
			Detail:    "totp_pending",
		})
		writeJSON(w, http.StatusAccepted, map[string]interface{}{
			"totp_required": true,
			"session_token": token,
		})
		return
	}

	_ = s.users.UpdateLastLogin(user.ID)

	s.audlog.Log(audit.Entry{
		EventType: audit.EventLogin,
		Username:  user.Username,
		TenantID:  user.TenantID,
		IPAddress: s.remoteIP(r),
		Success:   true,
	})

	http.SetCookie(w, &http.Cookie{
		Name:     sessionCookieName,
		Value:    token,
		Path:     "/",
		MaxAge:   8 * 3600,
		HttpOnly: true,
		SameSite: http.SameSiteStrictMode,
		Secure:   s.cfg.SecureCookies,
	})

	writeJSON(w, http.StatusOK, map[string]interface{}{
		"user": map[string]interface{}{
			"id":             user.ID,
			"username":       user.Username,
			"email":          user.Email,
			"role":           user.Role,
			"list_page_size": user.ListPageSize,
		},
	})
}

func (s *Server) handleMe(w http.ResponseWriter, r *http.Request) {
	sess := sessionFromCtx(r.Context())

	user, err := s.users.GetByUsername(sess.Username)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "user lookup failed")
		return
	}

	writeJSON(w, http.StatusOK, map[string]interface{}{
		"username":       user.Username,
		"email":          user.Email,
		"role":           user.Role,
		"list_page_size": user.ListPageSize,
	})
}

func (s *Server) handleLogout(w http.ResponseWriter, r *http.Request) {
	// Read token from cookie first, then Bearer header
	token := ""
	if c, err := r.Cookie(sessionCookieName); err == nil {
		token = c.Value
	}
	if token == "" {
		token = extractBearerToken(r)
	}
	if token != "" {
		_ = s.authMgr.Logout(token)
	}

	// Clear the session cookie
	http.SetCookie(w, &http.Cookie{
		Name:     sessionCookieName,
		Value:    "",
		Path:     "/",
		MaxAge:   -1,
		HttpOnly: true,
		SameSite: http.SameSiteStrictMode,
		Secure:   s.cfg.SecureCookies,
	})

	sess := sessionFromCtx(r.Context())
	s.audlog.Log(audit.Entry{
		EventType: audit.EventLogout,
		Username:  sess.Username,
		TenantID:  sess.TenantID,
		IPAddress: s.remoteIP(r),
		Success:   true,
	})

	writeJSON(w, http.StatusOK, map[string]bool{"ok": true})
}
