package auth

import (
	"net/http"
	"strings"

	"gitea.hostxtra.co.uk/mrhid6/vantage/admin/internal/audit"
	"gitea.hostxtra.co.uk/mrhid6/vantage/admin/internal/db"
	"gitea.hostxtra.co.uk/mrhid6/vantage/admin/internal/models"
	"github.com/gin-gonic/gin"
	"go.mongodb.org/mongo-driver/v2/bson"
	"golang.org/x/crypto/bcrypt"
)

// genericAuthError is returned for every failure mode — unknown email, wrong
// password, wrong role. Distinguishing them would confirm which addresses have
// accounts.
const genericAuthError = "email or password is incorrect"

func HandleStaffLogin(c *gin.Context) {
	var body struct {
		Email    string `json:"email"`
		Password string `json:"password"`
	}
	if err := c.ShouldBindJSON(&body); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "email and password are required"})
		return
	}
	email := strings.ToLower(strings.TrimSpace(body.Email))

	if !allowAttempt(email, c.ClientIP()) {
		c.JSON(http.StatusTooManyRequests, gin.H{"error": "too many attempts, try again later"})
		return
	}

	var u models.StaffUser
	err := db.Admin("staff_users").FindOne(c.Request.Context(), bson.M{"email": email}).Decode(&u)
	if err != nil {
		// Spend the same work as a real comparison so timing does not
		// distinguish "no such user" from "wrong password".
		bcrypt.CompareHashAndPassword([]byte(dummyHash), []byte(body.Password))
		audit.Write(c.Request.Context(), models.AuditEntry{
			Actor: email, Action: "staff.login_failed", IP: c.ClientIP(), Detail: "unknown email"})
		c.JSON(http.StatusUnauthorized, gin.H{"error": genericAuthError})
		return
	}

	if bcrypt.CompareHashAndPassword([]byte(u.PasswordHash), []byte(body.Password)) != nil {
		audit.Write(c.Request.Context(), models.AuditEntry{
			Actor: email, Action: "staff.login_failed", IP: c.ClientIP(), Detail: "bad password"})
		c.JSON(http.StatusUnauthorized, gin.H{"error": genericAuthError})
		return
	}

	id, err := Save(c.Request.Context(), Session{UserID: u.UserID, Kind: KindStaff, Email: u.Email})
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "session failed"})
		return
	}
	SetCookie(c, id)
	clearAttempts(email)
	audit.Write(c.Request.Context(), models.AuditEntry{
		Actor: email, Action: "staff.login", IP: c.ClientIP()})
	c.JSON(http.StatusOK, gin.H{"kind": KindStaff, "email": u.Email, "name": u.Name})
}

// dummyHash is a valid bcrypt hash of a random value, compared against when no
// user exists so the timing profile matches.
const dummyHash = "$2a$12$C6UzMDM.H6dfI/f/IKcEe.6qGZoAZQFtQmOoLmC5PbfW1uMh1Sv2u"

func HandleLogout(c *gin.Context) {
	if id, err := c.Cookie(CookieName); err == nil && id != "" {
		Destroy(c.Request.Context(), id)
	}
	ClearCookie(c)
	c.JSON(http.StatusOK, gin.H{"ok": true})
}
