package centrifugo

import (
	"time"

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

// TokenConfig JWT Token 配置
type TokenConfig struct {
	// Secret 用於簽名的密鑰（與 Centrifugo 配置的 token_hmac_secret_key 一致）
	Secret string
	// ExpireIn Token 過期時間（預設 1 小時）
	ExpireIn time.Duration
}

// TokenGenerator JWT Token 生成器
type TokenGenerator struct {
	config TokenConfig
}

// NewTokenGenerator 創建新的 Token 生成器
func NewTokenGenerator(secret string) *TokenGenerator {
	return &TokenGenerator{
		config: TokenConfig{
			Secret:   secret,
			ExpireIn: time.Hour,
		},
	}
}

// NewTokenGeneratorWithConfig 創建使用自定義配置的 Token 生成器
func NewTokenGeneratorWithConfig(config TokenConfig) *TokenGenerator {
	if config.ExpireIn == 0 {
		config.ExpireIn = time.Hour
	}
	return &TokenGenerator{
		config: config,
	}
}

// ConnectionClaims 連線 Token 的 Claims
type ConnectionClaims struct {
	jwt.RegisteredClaims
	// Sub 用戶 ID（必填）
	Sub string `json:"sub"`
	// Info 用戶資訊（可選，會在 presence 中顯示）
	Info map[string]interface{} `json:"info,omitempty"`
	// Channels 自動訂閱的頻道列表（可選）
	Channels []string `json:"channels,omitempty"`
	// TokenVersion Token 版本（用於批量撤銷）
	TokenVersion int64 `json:"tv,omitempty"`
}

// SubscriptionClaims 訂閱 Token 的 Claims（用於私有頻道）
type SubscriptionClaims struct {
	jwt.RegisteredClaims
	// Sub 用戶 ID（必填）
	Sub string `json:"sub"`
	// Channel 頻道名稱（必填）
	Channel string `json:"channel"`
	// Info 頻道特定的用戶資訊（可選）
	Info map[string]interface{} `json:"info,omitempty"`
	// TokenVersion Token 版本（用於批量撤銷）
	TokenVersion int64 `json:"tv,omitempty"`
}

// ConnectionTokenOptions 連線 Token 選項
type ConnectionTokenOptions struct {
	// UserID 用戶 ID（必填）
	UserID string
	// Info 用戶資訊（可選）
	Info map[string]interface{}
	// Channels 自動訂閱的頻道列表（可選）
	Channels []string
	// ExpireAt 自定義過期時間（可選，為空則使用預設）
	ExpireAt *time.Time
	// TokenVersion Token 版本（可選，用於黑名單機制）
	TokenVersion int64
}

// SubscriptionTokenOptions 訂閱 Token 選項
type SubscriptionTokenOptions struct {
	// UserID 用戶 ID（必填）
	UserID string
	// Channel 頻道名稱（必填）
	Channel string
	// Info 頻道特定的用戶資訊（可選）
	Info map[string]interface{}
	// ExpireAt 自定義過期時間（可選，為空則使用預設）
	ExpireAt *time.Time
	// TokenVersion Token 版本（可選，用於黑名單機制）
	TokenVersion int64
}

// TokenResult 生成 Token 的結果
type TokenResult struct {
	Token     string    // JWT Token 字串
	JTI       string    // JWT ID（用於撤銷單一 Token）
	ExpiresAt time.Time // 過期時間
}

// GenerateConnectionToken 生成連線 Token
// 用於前端建立 WebSocket 連線時的身份驗證
func (g *TokenGenerator) GenerateConnectionToken(opts ConnectionTokenOptions) (string, error) {
	result, err := g.GenerateConnectionTokenWithJTI(opts)
	if err != nil {
		return "", err
	}
	return result.Token, nil
}

// GenerateConnectionTokenWithJTI 生成連線 Token 並返回 JTI
// 用於需要支援單一 Token 撤銷的場景
func (g *TokenGenerator) GenerateConnectionTokenWithJTI(opts ConnectionTokenOptions) (*TokenResult, error) {
	now := time.Now()
	expireAt := now.Add(g.config.ExpireIn)
	if opts.ExpireAt != nil {
		expireAt = *opts.ExpireAt
	}

	// 生成唯一的 JTI
	jti := uuid.New().String()

	// 如果沒有指定 TokenVersion，使用當前時間戳
	tokenVersion := opts.TokenVersion
	if tokenVersion == 0 {
		tokenVersion = now.UnixNano()
	}

	claims := ConnectionClaims{
		RegisteredClaims: jwt.RegisteredClaims{
			ID:        jti,
			Subject:   opts.UserID,
			IssuedAt:  jwt.NewNumericDate(now),
			ExpiresAt: jwt.NewNumericDate(expireAt),
		},
		Sub:          opts.UserID,
		Info:         opts.Info,
		Channels:     opts.Channels,
		TokenVersion: tokenVersion,
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenStr, err := token.SignedString([]byte(g.config.Secret))
	if err != nil {
		return nil, err
	}

	return &TokenResult{
		Token:     tokenStr,
		JTI:       jti,
		ExpiresAt: expireAt,
	}, nil
}

// GenerateSubscriptionToken 生成訂閱 Token
// 用於訂閱私有頻道時的身份驗證
func (g *TokenGenerator) GenerateSubscriptionToken(opts SubscriptionTokenOptions) (string, error) {
	now := time.Now()
	expireAt := now.Add(g.config.ExpireIn)
	if opts.ExpireAt != nil {
		expireAt = *opts.ExpireAt
	}

	claims := SubscriptionClaims{
		RegisteredClaims: jwt.RegisteredClaims{
			Subject:   opts.UserID,
			IssuedAt:  jwt.NewNumericDate(now),
			ExpiresAt: jwt.NewNumericDate(expireAt),
		},
		Sub:     opts.UserID,
		Channel: opts.Channel,
		Info:    opts.Info,
	}

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

// GenerateAnonymousToken 生成匿名連線 Token
// 用於允許匿名用戶連線（但仍需要驗證）
func (g *TokenGenerator) GenerateAnonymousToken() (string, error) {
	return g.GenerateConnectionToken(ConnectionTokenOptions{
		UserID: "", // 空的 UserID 表示匿名用戶
	})
}

// QuickConnectionToken 快速生成連線 Token（只需用戶 ID）
func (g *TokenGenerator) QuickConnectionToken(userID string) (string, error) {
	return g.GenerateConnectionToken(ConnectionTokenOptions{
		UserID: userID,
	})
}

// QuickSubscriptionToken 快速生成訂閱 Token（只需用戶 ID 和頻道）
func (g *TokenGenerator) QuickSubscriptionToken(userID, channel string) (string, error) {
	return g.GenerateSubscriptionToken(SubscriptionTokenOptions{
		UserID:  userID,
		Channel: channel,
	})
}
