package jwt

import (
	"time"

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

	"security-center-backend/internal/config"
)

type Manager struct {
	cfg config.JWTConfig
}

type Claims struct {
	UserID      uint64   `json:"user_id"`
	Email       string   `json:"email"`
	Roles       []string `json:"roles"`
	Permissions []string `json:"permissions"`
	jwt.RegisteredClaims
	// —— Agent 归因（人类 JWT 为零值）——
	ActorType       string `json:"actor_type,omitempty"` // "user"|"agent"
	AgentID         uint64 `json:"agent_id,omitempty"`
	AgentName       string `json:"agent_name,omitempty"`
	DelegatedUserID uint64 `json:"delegated_user_id,omitempty"`
}

func NewManager(cfg config.JWTConfig) *Manager {
	return &Manager{cfg: cfg}
}

func (m *Manager) GenerateAccessToken(userID uint64, email string, roles []string, permissions []string) (string, error) {
	claims := Claims{
		UserID:      userID,
		Email:       email,
		Roles:       roles,
		Permissions: permissions,
		RegisteredClaims: jwt.RegisteredClaims{
			Issuer:    m.cfg.Issuer,
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(m.cfg.AccessTokenTTL)),
		},
	}
	return jwt.NewWithClaims(jwt.GetSigningMethod(m.cfg.SigningAlgorithm), claims).SignedString([]byte(m.cfg.AccessSecret))
}

func (m *Manager) Validate(tokenStr string) (*Claims, error) {
	token, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(m.cfg.AccessSecret), nil
	}, jwt.WithLeeway(time.Duration(m.cfg.ClockSkewInSeconds)*time.Second))
	if err != nil {
		return nil, err
	}
	if claims, ok := token.Claims.(*Claims); ok && token.Valid {
		return claims, nil
	}
	return nil, jwt.ErrTokenInvalidClaims
}

type RefreshClaims struct {
	UserID uint64 `json:"user_id"`
	jwt.RegisteredClaims
}

func (m *Manager) GenerateRefreshToken(userID uint64) (string, error) {
	claims := RefreshClaims{
		UserID: userID,
		RegisteredClaims: jwt.RegisteredClaims{
			Issuer:    m.cfg.Issuer,
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(m.cfg.RefreshTokenTTL)),
		},
	}
	return jwt.NewWithClaims(jwt.GetSigningMethod(m.cfg.SigningAlgorithm), claims).SignedString([]byte(m.cfg.RefreshSecret))
}

func (m *Manager) ValidateRefreshToken(tokenStr string) (*RefreshClaims, error) {
	token, err := jwt.ParseWithClaims(tokenStr, &RefreshClaims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(m.cfg.RefreshSecret), nil
	}, jwt.WithLeeway(time.Duration(m.cfg.ClockSkewInSeconds)*time.Second))
	if err != nil {
		return nil, err
	}
	if claims, ok := token.Claims.(*RefreshClaims); ok && token.Valid {
		return claims, nil
	}
	return nil, jwt.ErrTokenInvalidClaims
}

func (m *Manager) AccessTTL() time.Duration {
	return m.cfg.AccessTokenTTL
}

func (m *Manager) RefreshTTL() time.Duration {
	return m.cfg.RefreshTokenTTL
}
