// Package centrifugo 提供 Centrifugo 即時訊息服務的完整 Go 客戶端
//
// 功能包含:
//   - HTTP API 客戶端（發布訊息、訂閱管理、在線狀態等）
//   - JWT Token 生成（連線認證、私有頻道訂閱）
//   - Token 黑名單管理（撤銷單一 Token、撤銷用戶所有 Token）
//   - 在線狀態追蹤（Redis 或記憶體存儲）
//
// 基本使用:
//
//	// 創建服務實例
//	svc := centrifugo.NewService(centrifugo.ServiceConfig{
//	    APIURL:      "http://localhost:8000",
//	    APIKey:      "your-api-key",
//	    TokenSecret: "your-jwt-secret",
//	    Redis:       redisClient, // 可選，用於黑名單和在線狀態
//	})
//
//	// 發布訊息
//	svc.Client().PublishJSON(ctx, "chat:room-1", data)
//
//	// 生成 Token
//	token, _ := svc.Token().QuickConnectionToken("user-123")
//
//	// 撤銷用戶所有 Token 並踢出
//	svc.InvalidateUser(ctx, "user-123")
package centrifugo

import (
	"context"
	"time"

	"github.com/zeromicro/go-zero/core/stores/redis"
)

// Service Centrifugo 服務整合介面
// 提供 HTTP API、Token 生成、黑名單管理、在線狀態追蹤的統一入口
type Service struct {
	client    *Client
	token     *TokenGenerator
	blacklist *TokenBlacklist
	online    *OnlineManager
}

// ServiceConfig 服務配置
type ServiceConfig struct {
	// APIURL Centrifugo HTTP API 地址（必填）
	APIURL string
	// APIKey Centrifugo API 密鑰（必填）
	APIKey string
	// TokenSecret JWT Token 簽名密鑰（必填）
	TokenSecret string
	// TokenExpire Token 過期時間（預設 1 小時）
	TokenExpire time.Duration
	// Redis 客戶端（可選，用於黑名單和在線狀態）
	Redis *redis.Redis
	// ClientConfig HTTP 客戶端配置（可選）
	ClientConfig *ClientConfig
	// OnlineTTL 在線狀態過期時間（預設 5 分鐘）
	OnlineTTL time.Duration
	// KeyPrefix Redis key 前綴（預設 "centrifugo:"）
	KeyPrefix string
}

// NewService 創建 Centrifugo 服務實例
func NewService(cfg ServiceConfig) *Service {
	// 設定預設值
	if cfg.TokenExpire == 0 {
		cfg.TokenExpire = time.Hour
	}
	if cfg.OnlineTTL == 0 {
		cfg.OnlineTTL = 5 * time.Minute
	}
	if cfg.KeyPrefix == "" {
		cfg.KeyPrefix = "centrifugo:"
	}

	// 創建 HTTP 客戶端
	var client *Client
	if cfg.ClientConfig != nil {
		client = NewClientWithConfig(*cfg.ClientConfig)
	} else {
		client = NewClient(cfg.APIURL, cfg.APIKey)
	}

	// 創建 Token 生成器
	token := NewTokenGeneratorWithConfig(TokenConfig{
		Secret:   cfg.TokenSecret,
		ExpireIn: cfg.TokenExpire,
	})

	svc := &Service{
		client: client,
		token:  token,
	}

	// 如果有 Redis，創建黑名單管理器和在線狀態管理器
	if cfg.Redis != nil {
		svc.blacklist = NewTokenBlacklistWithPrefix(cfg.Redis, cfg.KeyPrefix+"blacklist:")
		store := NewRedisOnlineStoreWithPrefix(cfg.Redis, cfg.KeyPrefix+"online:")
		svc.online = NewOnlineManagerWithTTL(client, store, cfg.OnlineTTL)
	}

	return svc
}

// Client 返回 HTTP API 客戶端
func (s *Service) Client() *Client {
	return s.client
}

// Token 返回 Token 生成器
func (s *Service) Token() *TokenGenerator {
	return s.token
}

// Blacklist 返回黑名單管理器（可能為 nil）
func (s *Service) Blacklist() *TokenBlacklist {
	return s.blacklist
}

// Online 返回在線狀態管理器（可能為 nil）
func (s *Service) Online() *OnlineManager {
	return s.online
}

// ==================== 便捷方法 ====================

// PublishJSON 發布 JSON 訊息到頻道
func (s *Service) PublishJSON(ctx context.Context, channel string, data interface{}) (*PublishResult, error) {
	return s.client.PublishJSON(ctx, channel, data)
}

// BroadcastJSON 批量發布 JSON 訊息到多個頻道
func (s *Service) BroadcastJSON(ctx context.Context, channels []string, data interface{}) error {
	return s.client.BroadcastJSON(ctx, channels, data)
}

// Disconnect 斷開用戶連線
func (s *Service) Disconnect(ctx context.Context, userID string) error {
	return s.client.Disconnect(ctx, userID)
}

// GenerateToken 快速生成連線 Token
func (s *Service) GenerateToken(userID string) (string, error) {
	return s.token.QuickConnectionToken(userID)
}

// GenerateTokenWithInfo 生成帶用戶資訊的連線 Token
func (s *Service) GenerateTokenWithInfo(userID string, info map[string]interface{}) (string, error) {
	return s.token.GenerateConnectionToken(ConnectionTokenOptions{
		UserID: userID,
		Info:   info,
	})
}

// InvalidateUser 撤銷用戶所有 Token 並斷開連線
// 這是最常用的「踢人」方法，適用於：
//   - 用戶被封禁
//   - 密碼變更
//   - 用戶登出（全設備）
func (s *Service) InvalidateUser(ctx context.Context, userID string) error {
	// 撤銷所有 Token
	if s.blacklist != nil {
		if err := s.blacklist.RevokeUserTokens(ctx, userID); err != nil {
			return err
		}
	}
	// 斷開連線
	return s.client.Disconnect(ctx, userID)
}

// IsUserOnline 檢查用戶是否在線
func (s *Service) IsUserOnline(ctx context.Context, userID string) (bool, error) {
	if s.online == nil {
		return false, ErrOnlineStoreNotConfigured
	}
	return s.online.IsUserOnline(ctx, userID)
}

// GetUsersOnlineStatus 批量獲取用戶在線狀態
func (s *Service) GetUsersOnlineStatus(ctx context.Context, userIDs []string) (map[string]bool, error) {
	if s.online == nil {
		return nil, ErrOnlineStoreNotConfigured
	}
	return s.online.GetUsersOnlineStatus(ctx, userIDs)
}

