package middleware

import (
	"log"
	"net/http"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"github.com/google/uuid"
)

// AgentClaims represents JWT claims for agent authentication
type AgentClaims struct {
	AgentID uuid.UUID `json:"agent_id"`
	jwt.RegisteredClaims
}

// JWTSecret is set by the server at initialization
var JWTSecret string

// JWT issuer constants for token type differentiation (F-A3-12 fix)
const (
	JWTIssuerAgent = "redflag-agent"
	JWTIssuerWeb   = "redflag-web"
)

// GenerateAgentToken creates a new JWT token for an agent
func GenerateAgentToken(agentID uuid.UUID) (string, error) {
	claims := AgentClaims{
		AgentID: agentID,
		RegisteredClaims: jwt.RegisteredClaims{
			Issuer:    JWTIssuerAgent,
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
		},
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString([]byte(JWTSecret))
}

// AuthMiddleware validates JWT tokens from agents
func AuthMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		authHeader := c.GetHeader("Authorization")
		if authHeader == "" {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "missing authorization header"})
			c.Abort()
			return
		}

		tokenString := strings.TrimPrefix(authHeader, "Bearer ")
		if tokenString == authHeader {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid authorization format"})
			c.Abort()
			return
		}

		token, err := jwt.ParseWithClaims(tokenString, &AgentClaims{}, func(token *jwt.Token) (interface{}, error) {
			return []byte(JWTSecret), nil
		})

		if err != nil || !token.Valid {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
			c.Abort()
			return
		}

		if claims, ok := token.Claims.(*AgentClaims); ok {
			// F-A3-12: Validate issuer to prevent cross-type token confusion
			if claims.Issuer != "" && claims.Issuer != JWTIssuerAgent {
				log.Printf("[WARNING] [server] [auth] wrong_token_issuer expected=%s got=%s", JWTIssuerAgent, claims.Issuer)
				c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token type"})
				c.Abort()
				return
			}
			// TODO: remove issuer-absent grace period after 30 days (backward compat for deployed agents)
			if claims.Issuer == "" {
				log.Printf("[WARNING] [server] [auth] agent_token_missing_issuer agent_id=%s", claims.AgentID)
			}
			c.Set("agent_id", claims.AgentID)
			c.Next()
		} else {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token claims"})
			c.Abort()
		}
	}
}
