package utils

import (
	"errors"
	"time"

	"github.com/echochat/backend/config"
	"github.com/golang-jwt/jwt/v5"
)

// Claims 自定义 JWT Claims，在标准 Claims 基础上扩展业务字段
type Claims struct {
	UserID     int64    `json:"user_id"`     // 用户 ID
	Username   string   `json:"username"`    // 用户名
	Roles      []string `json:"roles"`       // 用户角色代码列表
	ClientType string   `json:"client_type"` // 客户端类型：frontend / admin
	jwt.RegisteredClaims
}

var (
	ErrTokenExpired = errors.New("token 已过期")
	ErrTokenInvalid = errors.New("token 无效")
)

// GenerateToken 生成 Access Token
// 包含 UserID、Username、Roles、ClientType，有效期由配置中的 access_expire_min 决定
func GenerateToken(cfg *config.JWTConfig, userID int64, username string, roles []string, clientType string) (string, error) {
	expireTime := time.Now().Add(time.Duration(cfg.AccessExpireMin) * time.Minute)

	claims := &Claims{
		UserID:     userID,
		Username:   username,
		Roles:      roles,
		ClientType: clientType,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(expireTime),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Issuer:    cfg.Issuer,
			Subject:   "access",
		},
	}

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

// GenerateRefreshToken 生成 Refresh Token
// 包含 UserID 和 ClientType，有效期由配置中的 refresh_expire_day 决定
func GenerateRefreshToken(cfg *config.JWTConfig, userID int64, clientType string) (string, error) {
	expireTime := time.Now().Add(time.Duration(cfg.RefreshExpireDay) * 24 * time.Hour)

	claims := &Claims{
		UserID:     userID,
		ClientType: clientType,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(expireTime),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Issuer:    cfg.Issuer,
			Subject:   "refresh",
		},
	}

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

// ParseToken 解析并验证 JWT Token
// 验证签名、过期时间、签发者，返回解析后的 Claims
func ParseToken(cfg *config.JWTConfig, tokenStr string) (*Claims, error) {
	token, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, ErrTokenInvalid
		}
		return []byte(cfg.Secret), nil
	})
	if err != nil {
		if errors.Is(err, jwt.ErrTokenExpired) {
			return nil, ErrTokenExpired
		}
		return nil, ErrTokenInvalid
	}

	claims, ok := token.Claims.(*Claims)
	if !ok || !token.Valid {
		return nil, ErrTokenInvalid
	}

	return claims, nil
}
