// Package auth provides local JWT-based authentication replacing Keycloak OIDC.
// Tokens are signed with HMAC-SHA256 using a secret configured via VEYLANT_AUTH_JWT_SECRET.
package auth

import (
	"context"
	"errors"
	"fmt"
	"time"

	"github.com/golang-jwt/jwt/v5"

	"github.com/veylant/ia-gateway/internal/middleware"
)

// LocalJWTVerifier implements middleware.TokenVerifier using self-signed HS256 tokens.
type LocalJWTVerifier struct {
	secret []byte
}

// NewLocalJWTVerifier creates a verifier that validates tokens signed with secret.
func NewLocalJWTVerifier(secret string) *LocalJWTVerifier {
	return &LocalJWTVerifier{secret: []byte(secret)}
}

// veylantClaims is the JWT payload structure for tokens we issue.
type veylantClaims struct {
	Email      string   `json:"email"`
	TenantID   string   `json:"tenant_id"`
	Roles      []string `json:"roles"`
	Department string   `json:"department,omitempty"`
	Name       string   `json:"name,omitempty"`
	jwt.RegisteredClaims
}

// Verify implements middleware.TokenVerifier.
func (v *LocalJWTVerifier) Verify(_ context.Context, rawToken string) (*middleware.UserClaims, error) {
	token, err := jwt.ParseWithClaims(rawToken, &veylantClaims{}, func(t *jwt.Token) (interface{}, error) {
		if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
		}
		return v.secret, nil
	})
	if err != nil {
		return nil, fmt.Errorf("token verification failed: %w", err)
	}

	claims, ok := token.Claims.(*veylantClaims)
	if !ok || !token.Valid {
		return nil, errors.New("invalid token claims")
	}

	return &middleware.UserClaims{
		UserID:     claims.Subject,
		TenantID:   claims.TenantID,
		Email:      claims.Email,
		Roles:      claims.Roles,
		Department: claims.Department,
	}, nil
}

// GenerateToken creates a signed JWT for the given claims.
func GenerateToken(claims *middleware.UserClaims, name, secret string, ttlHours int) (string, error) {
	now := time.Now()
	jwtClaims := veylantClaims{
		Email:      claims.Email,
		TenantID:   claims.TenantID,
		Roles:      claims.Roles,
		Department: claims.Department,
		Name:       name,
		RegisteredClaims: jwt.RegisteredClaims{
			Subject:   claims.UserID,
			IssuedAt:  jwt.NewNumericDate(now),
			ExpiresAt: jwt.NewNumericDate(now.Add(time.Duration(ttlHours) * time.Hour)),
		},
	}

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