package middleware

import (
	"net/http"
	"strings"

	"git.mchus.pro/mchus/quoteforge/internal/models"
	"git.mchus.pro/mchus/quoteforge/internal/services"
	"github.com/gin-gonic/gin"
)

const (
	AuthUserKey   = "auth_user"
	AuthClaimsKey = "auth_claims"
)

func Auth(authService *services.AuthService) gin.HandlerFunc {
	return func(c *gin.Context) {
		authHeader := c.GetHeader("Authorization")
		if authHeader == "" {
			c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
				"error": "authorization header required",
			})
			return
		}

		parts := strings.SplitN(authHeader, " ", 2)
		if len(parts) != 2 || parts[0] != "Bearer" {
			c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
				"error": "invalid authorization header format",
			})
			return
		}

		claims, err := authService.ValidateToken(parts[1])
		if err != nil {
			c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
				"error": err.Error(),
			})
			return
		}

		c.Set(AuthClaimsKey, claims)
		c.Next()
	}
}

func RequireRole(roles ...models.UserRole) gin.HandlerFunc {
	return func(c *gin.Context) {
		claims, exists := c.Get(AuthClaimsKey)
		if !exists {
			c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
				"error": "authentication required",
			})
			return
		}

		authClaims := claims.(*services.Claims)

		for _, role := range roles {
			if authClaims.Role == role {
				c.Next()
				return
			}
		}

		c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
			"error": "insufficient permissions",
		})
	}
}

func RequireEditor() gin.HandlerFunc {
	return RequireRole(models.RoleEditor, models.RolePricingAdmin, models.RoleAdmin)
}

func RequirePricingAdmin() gin.HandlerFunc {
	return RequireRole(models.RolePricingAdmin, models.RoleAdmin)
}

func RequireAdmin() gin.HandlerFunc {
	return RequireRole(models.RoleAdmin)
}

// GetClaims extracts auth claims from context
func GetClaims(c *gin.Context) *services.Claims {
	claims, exists := c.Get(AuthClaimsKey)
	if !exists {
		return nil
	}
	return claims.(*services.Claims)
}

// GetUserID extracts user ID from context
func GetUserID(c *gin.Context) uint {
	claims := GetClaims(c)
	if claims == nil {
		return 0
	}
	return claims.UserID
}

// GetUsername extracts username from context
func GetUsername(c *gin.Context) string {
	claims := GetClaims(c)
	if claims == nil {
		return ""
	}
	return claims.Username
}
