package centrifugo

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
	"time"
)

// Client Centrifugo 客戶端
type Client struct {
	apiURL string
	apiKey string
	client *http.Client
}

// ClientConfig 客戶端配置
type ClientConfig struct {
	// APIURL Centrifugo API 地址（必填）
	APIURL string
	// APIKey API 密鑰（必填）
	APIKey string

	// Timeout 整體請求超時時間（預設 10 秒）
	Timeout time.Duration
	// MaxIdleConns 最大閒置連線數（預設 100）
	MaxIdleConns int
	// MaxIdleConnsPerHost 每個 host 最大閒置連線數（預設 20，適合高併發）
	MaxIdleConnsPerHost int
	// IdleConnTimeout 閒置連線超時時間（預設 90 秒）
	IdleConnTimeout time.Duration
	// DialTimeout 建立連線超時時間（預設 5 秒）
	DialTimeout time.Duration
	// TLSHandshakeTimeout TLS 握手超時時間（預設 5 秒）
	TLSHandshakeTimeout time.Duration
	// ResponseHeaderTimeout 等待響應頭超時時間（預設 10 秒）
	ResponseHeaderTimeout time.Duration
}

// DefaultConfig 返回預設配置
func DefaultConfig(apiURL, apiKey string) ClientConfig {
	return ClientConfig{
		APIURL:                apiURL,
		APIKey:                apiKey,
		Timeout:               10 * time.Second,
		MaxIdleConns:          100,
		MaxIdleConnsPerHost:   20, // 提高以支援高併發
		IdleConnTimeout:       90 * time.Second,
		DialTimeout:           5 * time.Second,
		TLSHandshakeTimeout:   5 * time.Second,
		ResponseHeaderTimeout: 10 * time.Second,
	}
}

// HighPerformanceConfig 返回高效能配置（適合高併發場景）
func HighPerformanceConfig(apiURL, apiKey string) ClientConfig {
	return ClientConfig{
		APIURL:                apiURL,
		APIKey:                apiKey,
		Timeout:               5 * time.Second,  // 更短的超時
		MaxIdleConns:          200,              // 更多閒置連線
		MaxIdleConnsPerHost:   50,               // 更多每 host 連線
		IdleConnTimeout:       120 * time.Second,
		DialTimeout:           3 * time.Second,
		TLSHandshakeTimeout:   3 * time.Second,
		ResponseHeaderTimeout: 5 * time.Second,
	}
}

// NewClient 創建新的 Centrifugo 客戶端（使用預設配置）
func NewClient(apiURL, apiKey string) *Client {
	return NewClientWithConfig(DefaultConfig(apiURL, apiKey))
}

// NewClientWithConfig 創建使用自定義配置的 Centrifugo 客戶端
func NewClientWithConfig(config ClientConfig) *Client {
	// 設定預設值
	if config.Timeout == 0 {
		config.Timeout = 10 * time.Second
	}
	if config.MaxIdleConns == 0 {
		config.MaxIdleConns = 100
	}
	if config.MaxIdleConnsPerHost == 0 {
		config.MaxIdleConnsPerHost = 20
	}
	if config.IdleConnTimeout == 0 {
		config.IdleConnTimeout = 90 * time.Second
	}
	if config.DialTimeout == 0 {
		config.DialTimeout = 5 * time.Second
	}
	if config.TLSHandshakeTimeout == 0 {
		config.TLSHandshakeTimeout = 5 * time.Second
	}
	if config.ResponseHeaderTimeout == 0 {
		config.ResponseHeaderTimeout = 10 * time.Second
	}

	transport := &http.Transport{
		DialContext: (&net.Dialer{
			Timeout:   config.DialTimeout,
			KeepAlive: 30 * time.Second, // TCP keep-alive
		}).DialContext,
		MaxIdleConns:          config.MaxIdleConns,
		MaxIdleConnsPerHost:   config.MaxIdleConnsPerHost,
		IdleConnTimeout:       config.IdleConnTimeout,
		TLSHandshakeTimeout:   config.TLSHandshakeTimeout,
		ResponseHeaderTimeout: config.ResponseHeaderTimeout,
		ForceAttemptHTTP2:     true, // 嘗試使用 HTTP/2
	}

	return &Client{
		apiURL: config.APIURL,
		apiKey: config.APIKey,
		client: &http.Client{
			Timeout:   config.Timeout,
			Transport: transport,
		},
	}
}

// NewClientWithHTTP 創建使用自定義 HTTP 客戶端的 Centrifugo 客戶端
func NewClientWithHTTP(apiURL, apiKey string, httpClient *http.Client) *Client {
	return &Client{
		apiURL: apiURL,
		apiKey: apiKey,
		client: httpClient,
	}
}

// ==================== Request/Response 結構 ====================

// PublishRequest 發布請求
type PublishRequest struct {
	Channel string      `json:"channel"`
	Data    interface{} `json:"data"`
}

// BroadcastRequest 批量發布請求
type BroadcastRequest struct {
	Channels []string    `json:"channels"`
	Data     interface{} `json:"data"`
}

// SubscribeRequest 訂閱請求
type SubscribeRequest struct {
	User    string `json:"user"`
	Channel string `json:"channel"`
}

// UnsubscribeRequest 取消訂閱請求
type UnsubscribeRequest struct {
	User    string `json:"user"`
	Channel string `json:"channel"`
}

// DisconnectRequest 斷開連線請求
type DisconnectRequest struct {
	User string `json:"user"`
}

// DisconnectWithCodeRequest 帶代碼的斷開連線請求
type DisconnectWithCodeRequest struct {
	User       string         `json:"user"`
	Disconnect DisconnectInfo `json:"disconnect,omitempty"`
}

// DisconnectInfo 斷開連線資訊
type DisconnectInfo struct {
	Code   uint32 `json:"code"`
	Reason string `json:"reason"`
}

// PresenceRequest 在線狀態請求
type PresenceRequest struct {
	Channel string `json:"channel"`
}

// PresenceStatsRequest 在線統計請求
type PresenceStatsRequest struct {
	Channel string `json:"channel"`
}

// HistoryRequest 歷史訊息請求
type HistoryRequest struct {
	Channel string `json:"channel"`
	Limit   int    `json:"limit,omitempty"`
	Reverse bool   `json:"reverse,omitempty"`
}

// ChannelsRequest 頻道列表請求
type ChannelsRequest struct {
	Pattern string `json:"pattern,omitempty"`
}

// InfoRequest 伺服器資訊請求
type InfoRequest struct{}

// APIResponse Centrifugo API 通用響應
type APIResponse struct {
	Error  *APIError   `json:"error,omitempty"`
	Result interface{} `json:"result,omitempty"`
}

// APIError Centrifugo API 錯誤
type APIError struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

func (e *APIError) Error() string {
	return fmt.Sprintf("centrifugo error %d: %s", e.Code, e.Message)
}

// PublishResult 發布結果
type PublishResult struct {
	Offset uint64 `json:"offset,omitempty"`
	Epoch  string `json:"epoch,omitempty"`
}

// PresenceResult 在線狀態結果
type PresenceResult struct {
	Presence map[string]ClientInfo `json:"presence"`
}

// ClientInfo 客戶端資訊
type ClientInfo struct {
	User     string          `json:"user"`
	Client   string          `json:"client"`
	ConnInfo json.RawMessage `json:"conn_info,omitempty"`
	ChanInfo json.RawMessage `json:"chan_info,omitempty"`
}

// PresenceStatsResult 在線統計結果
type PresenceStatsResult struct {
	NumClients int `json:"num_clients"`
	NumUsers   int `json:"num_users"`
}

// HistoryResult 歷史訊息結果
type HistoryResult struct {
	Publications []Publication `json:"publications"`
	Offset       uint64        `json:"offset,omitempty"`
	Epoch        string        `json:"epoch,omitempty"`
}

// Publication 發布的訊息
type Publication struct {
	Offset uint64          `json:"offset,omitempty"`
	Data   json.RawMessage `json:"data"`
	Info   *ClientInfo     `json:"info,omitempty"`
}

// ChannelsResult 頻道列表結果
type ChannelsResult struct {
	Channels map[string]ChannelInfo `json:"channels"`
}

// ChannelInfo 頻道資訊
type ChannelInfo struct {
	NumClients int `json:"num_clients"`
}

// InfoResult 伺服器資訊結果
type InfoResult struct {
	Nodes []NodeInfo `json:"nodes"`
}

// NodeInfo 節點資訊
type NodeInfo struct {
	UID         string `json:"uid"`
	Name        string `json:"name"`
	Version     string `json:"version"`
	NumClients  int    `json:"num_clients"`
	NumUsers    int    `json:"num_users"`
	NumChannels int    `json:"num_channels"`
	Uptime      int    `json:"uptime"`
}

// ==================== 發布相關方法 ====================

// Publish 發布訊息到指定頻道
func (c *Client) Publish(ctx context.Context, channel string, data []byte) (*PublishResult, error) {
	req := PublishRequest{
		Channel: channel,
		Data:    json.RawMessage(data),
	}
	return c.publish(ctx, req)
}

// PublishJSON 發布 JSON 訊息到指定頻道
func (c *Client) PublishJSON(ctx context.Context, channel string, data interface{}) (*PublishResult, error) {
	req := PublishRequest{
		Channel: channel,
		Data:    data,
	}
	return c.publish(ctx, req)
}

func (c *Client) publish(ctx context.Context, req PublishRequest) (*PublishResult, error) {
	var result PublishResult
	if err := c.callAPI(ctx, "publish", req, &result); err != nil {
		return nil, err
	}
	return &result, nil
}

// Broadcast 批量發布訊息到多個頻道
func (c *Client) Broadcast(ctx context.Context, channels []string, data []byte) error {
	req := BroadcastRequest{
		Channels: channels,
		Data:     json.RawMessage(data),
	}
	return c.callAPI(ctx, "broadcast", req, nil)
}

// BroadcastJSON 批量發布 JSON 訊息到多個頻道
func (c *Client) BroadcastJSON(ctx context.Context, channels []string, data interface{}) error {
	req := BroadcastRequest{
		Channels: channels,
		Data:     data,
	}
	return c.callAPI(ctx, "broadcast", req, nil)
}

// ==================== 訂閱管理方法 ====================

// Subscribe 訂閱用戶到頻道
func (c *Client) Subscribe(ctx context.Context, user, channel string) error {
	req := SubscribeRequest{
		User:    user,
		Channel: channel,
	}
	return c.callAPI(ctx, "subscribe", req, nil)
}

// Unsubscribe 取消用戶訂閱
func (c *Client) Unsubscribe(ctx context.Context, user, channel string) error {
	req := UnsubscribeRequest{
		User:    user,
		Channel: channel,
	}
	return c.callAPI(ctx, "unsubscribe", req, nil)
}

// Disconnect 強制斷開用戶連線
func (c *Client) Disconnect(ctx context.Context, user string) error {
	req := DisconnectRequest{
		User: user,
	}
	return c.callAPI(ctx, "disconnect", req, nil)
}

// DisconnectWithCode 強制斷開用戶連線（帶斷開代碼和原因）
func (c *Client) DisconnectWithCode(ctx context.Context, user string, code uint32, reason string) error {
	req := DisconnectWithCodeRequest{
		User: user,
		Disconnect: DisconnectInfo{
			Code:   code,
			Reason: reason,
		},
	}
	return c.callAPI(ctx, "disconnect", req, nil)
}


// ==================== 在線狀態方法 ====================

// Presence 獲取頻道在線用戶
func (c *Client) Presence(ctx context.Context, channel string) (*PresenceResult, error) {
	req := PresenceRequest{
		Channel: channel,
	}
	var result PresenceResult
	if err := c.callAPI(ctx, "presence", req, &result); err != nil {
		return nil, err
	}
	return &result, nil
}

// PresenceStats 獲取頻道在線統計
func (c *Client) PresenceStats(ctx context.Context, channel string) (*PresenceStatsResult, error) {
	req := PresenceStatsRequest{
		Channel: channel,
	}
	var result PresenceStatsResult
	if err := c.callAPI(ctx, "presence_stats", req, &result); err != nil {
		return nil, err
	}
	return &result, nil
}

// ==================== 歷史訊息方法 ====================

// History 獲取頻道歷史訊息
func (c *Client) History(ctx context.Context, channel string, limit int) (*HistoryResult, error) {
	req := HistoryRequest{
		Channel: channel,
		Limit:   limit,
	}
	var result HistoryResult
	if err := c.callAPI(ctx, "history", req, &result); err != nil {
		return nil, err
	}
	return &result, nil
}

// HistoryReverse 獲取頻道歷史訊息（倒序）
func (c *Client) HistoryReverse(ctx context.Context, channel string, limit int) (*HistoryResult, error) {
	req := HistoryRequest{
		Channel: channel,
		Limit:   limit,
		Reverse: true,
	}
	var result HistoryResult
	if err := c.callAPI(ctx, "history", req, &result); err != nil {
		return nil, err
	}
	return &result, nil
}

// ==================== 頻道管理方法 ====================

// Channels 獲取所有活躍頻道
func (c *Client) Channels(ctx context.Context) (*ChannelsResult, error) {
	req := ChannelsRequest{}
	var result ChannelsResult
	if err := c.callAPI(ctx, "channels", req, &result); err != nil {
		return nil, err
	}
	return &result, nil
}

// ChannelsWithPattern 獲取匹配模式的活躍頻道
func (c *Client) ChannelsWithPattern(ctx context.Context, pattern string) (*ChannelsResult, error) {
	req := ChannelsRequest{
		Pattern: pattern,
	}
	var result ChannelsResult
	if err := c.callAPI(ctx, "channels", req, &result); err != nil {
		return nil, err
	}
	return &result, nil
}

// ==================== 伺服器狀態方法 ====================

// Info 獲取伺服器狀態資訊
func (c *Client) Info(ctx context.Context) (*InfoResult, error) {
	req := InfoRequest{}
	var result InfoResult
	if err := c.callAPI(ctx, "info", req, &result); err != nil {
		return nil, err
	}
	return &result, nil
}

// Ping 檢查伺服器是否健康
func (c *Client) Ping(ctx context.Context) error {
	_, err := c.Info(ctx)
	return err
}

// ==================== 內部方法 ====================

// callAPI 調用 Centrifugo API
func (c *Client) callAPI(ctx context.Context, method string, params interface{}, result interface{}) error {
	body, err := json.Marshal(params)
	if err != nil {
		return fmt.Errorf("failed to marshal request: %w", err)
	}

	url := fmt.Sprintf("%s/api/%s", c.apiURL, method)
	httpReq, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(body))
	if err != nil {
		return fmt.Errorf("failed to create request: %w", err)
	}

	httpReq.Header.Set("Content-Type", "application/json")
	if c.apiKey != "" {
		httpReq.Header.Set("Authorization", "apikey "+c.apiKey)
	}

	resp, err := c.client.Do(httpReq)
	if err != nil {
		return fmt.Errorf("failed to send request: %w", err)
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("failed to read response: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("centrifugo returned status %d: %s", resp.StatusCode, string(respBody))
	}

	// 解析響應
	var apiResp APIResponse
	if result != nil {
		apiResp.Result = result
	}

	if err := json.Unmarshal(respBody, &apiResp); err != nil {
		return fmt.Errorf("failed to unmarshal response: %w", err)
	}

	if apiResp.Error != nil {
		return apiResp.Error
	}

	return nil
}
