package auth

import (
	"net/http"

	"github.com/gin-gonic/gin"

	"dragonrelay/internal/db"
)

// LevelRank returns a numeric rank for a host-access level so that levels can
// be compared ordinally.  Unknown strings map to 0, which is below every named
// level.
func LevelRank(level string) int {
	switch level {
	case "view":
		return 1
	case "connect":
		return 2
	case "manage":
		return 3
	default:
		return 0
	}
}

// RequireHostAccess returns a Gin middleware that enforces per-host ACL checks.
// The host IP is read from the ":ip" route parameter.
//
// Resolution order:
//  1. No claims in context        → 401 Unauthorized
//  2. Role "admin"                → always allowed (bypass ACL)
//  3. Role "viewer" AND minLevel
//     is higher than "view"       → 403 Forbidden (viewers are capped)
//  4. ACL lookup for (user, host) → 403 if not found or rank too low
func RequireHostAccess(store *db.Store, minLevel string) gin.HandlerFunc {
	return func(c *gin.Context) {
		claims := GetClaims(c)
		if claims == nil {
			c.AbortWithStatus(http.StatusUnauthorized)
			return
		}

		// Admins bypass all per-host ACL rules.
		if claims.Role == "admin" {
			c.Next()
			return
		}

		// Viewers are hard-capped at the "view" level; reject any request
		// that requires a higher privilege.
		if claims.Role == "viewer" && LevelRank(minLevel) > LevelRank("view") {
			c.AbortWithStatus(http.StatusForbidden)
			return
		}

		// Look up the requesting user.
		user, err := store.GetUserByUsername(claims.Username)
		if err != nil || user == nil {
			c.AbortWithStatus(http.StatusForbidden)
			return
		}

		// Check the host ACL entry.
		hostIP := c.Param("ip")
		level, ok, err := store.GetHostAccess(user.ID, hostIP)
		if err != nil || !ok || LevelRank(level) < LevelRank(minLevel) {
			c.AbortWithStatus(http.StatusForbidden)
			return
		}

		c.Next()
	}
}
