package websocket

import (
	"fmt"
	"time"
)

// DetailedStats menyimpan statistik detail WebSocket
type DetailedStats struct {
	ConnectedClients int            `json:"connected_clients"`
	UniqueIPs        int            `json:"unique_ips"`
	StaticClients    int            `json:"static_clients"`
	ActiveRooms      int            `json:"active_rooms"`
	IPDistribution   map[string]int `json:"ip_distribution"`
	RoomDistribution map[string]int `json:"room_distribution"`
	MessageQueueSize int            `json:"message_queue_size"`
	QueueWorkers     int            `json:"queue_workers"`
	Uptime           time.Duration  `json:"uptime_seconds"`
	Timestamp        int64          `json:"timestamp"`
}

// ClientInfo menyimpan informasi klien
type ClientInfo struct {
	ID          string    `json:"id"`
	StaticID    string    `json:"static_id"`
	IPAddress   string    `json:"ip_address"`
	UserID      string    `json:"user_id"`
	Room        string    `json:"room"`
	ConnectedAt time.Time `json:"connected_at"`
	LastPing    time.Time `json:"last_ping"`
	LastPong    time.Time `json:"last_pong"`
	IsActive    bool      `json:"is_active"`
}

// MonitoringData menyimpan data monitoring lengkap
type MonitoringData struct {
	Stats          DetailedStats          `json:"stats"`
	RecentActivity []ActivityLog          `json:"recent_activity"`
	SystemHealth   map[string]interface{} `json:"system_health"`
	Performance    PerformanceMetrics     `json:"performance"`
}

// PerformanceMetrics menyimpan metrik performa
type PerformanceMetrics struct {
	MessagesPerSecond float64 `json:"messages_per_second"`
	AverageLatency    float64 `json:"average_latency_ms"`
	ErrorRate         float64 `json:"error_rate_percent"`
	MemoryUsage       int64   `json:"memory_usage_bytes"`
}

// MonitoringManager mengelola monitoring WebSocket
type MonitoringManager struct {
	hub *Hub
}

// NewMonitoringManager membuat manajer monitoring baru
func NewMonitoringManager(hub *Hub) *MonitoringManager {
	return &MonitoringManager{hub: hub}
}

// GetDetailedStats mengembalikan statistik detail
func (m *MonitoringManager) GetDetailedStats() DetailedStats {
	m.hub.mu.RLock()
	defer m.hub.mu.RUnlock()

	// Hitung distribusi IP
	ipDistribution := make(map[string]int)
	for ip, clients := range m.hub.clientsByIP {
		ipDistribution[ip] = len(clients)
	}

	// Hitung distribusi ruangan
	roomDistribution := make(map[string]int)
	for room, clients := range m.hub.rooms {
		roomDistribution[room] = len(clients)
	}

	return DetailedStats{
		ConnectedClients: len(m.hub.clients),
		UniqueIPs:        len(m.hub.clientsByIP),
		StaticClients:    len(m.hub.clientsByStatic),
		ActiveRooms:      len(m.hub.rooms),
		IPDistribution:   ipDistribution,
		RoomDistribution: roomDistribution,
		MessageQueueSize: len(m.hub.messageQueue),
		QueueWorkers:     m.hub.config.QueueWorkers,
		Uptime:           time.Since(m.hub.startTime),
		Timestamp:        time.Now().Unix(),
	}
}

// GetAllClients mengembalikan semua klien yang terhubung
func (m *MonitoringManager) GetAllClients() []ClientInfo {
	m.hub.mu.RLock()
	defer m.hub.mu.RUnlock()

	var clients []ClientInfo
	for client := range m.hub.clients {
		clientInfo := ClientInfo{
			ID:          client.ID,
			StaticID:    client.StaticID,
			IPAddress:   client.IPAddress,
			UserID:      client.UserID,
			Room:        client.Room,
			ConnectedAt: client.connectedAt,
			LastPing:    client.lastPing,
			LastPong:    client.lastPong,
			IsActive:    client.isClientActive(),
		}
		clients = append(clients, clientInfo)
	}

	return clients
}

// GetMonitoringData mengembalikan data monitoring lengkap
func (m *MonitoringManager) GetMonitoringData() MonitoringData {
	stats := m.GetDetailedStats()

	m.hub.activityMu.RLock()
	recentActivity := make([]ActivityLog, 0)
	// Dapatkan 100 aktivitas terakhir
	start := len(m.hub.activityLog) - 100
	if start < 0 {
		start = 0
	}
	for i := start; i < len(m.hub.activityLog); i++ {
		recentActivity = append(recentActivity, m.hub.activityLog[i])
	}
	m.hub.activityMu.RUnlock()

	// Dapatkan kesehatan sistem dari layanan database
	systemHealth := make(map[string]interface{})
	if m.hub.dbService != nil {
		systemHealth["databases"] = m.hub.dbService.Health()
		systemHealth["available_dbs"] = m.hub.dbService.ListDBs()
	}
	systemHealth["websocket_status"] = "healthy"
	systemHealth["uptime_seconds"] = time.Since(m.hub.startTime).Seconds()

	// Hitung metrik performa
	uptime := time.Since(m.hub.startTime)
	var messagesPerSecond float64
	var errorRate float64

	if uptime.Seconds() > 0 {
		messagesPerSecond = float64(m.hub.messageCount) / uptime.Seconds()
	}

	if m.hub.messageCount > 0 {
		errorRate = (float64(m.hub.errorCount) / float64(m.hub.messageCount)) * 100
	}

	performance := PerformanceMetrics{
		MessagesPerSecond: messagesPerSecond,
		AverageLatency:    2.5, // Nilai mock - implementasi pelacakan latensi aktual
		ErrorRate:         errorRate,
		MemoryUsage:       0, // Nilai mock - implementasi pelacakan memori aktual
	}

	return MonitoringData{
		Stats:          stats,
		RecentActivity: recentActivity,
		SystemHealth:   systemHealth,
		Performance:    performance,
	}
}

// CleanupInactiveClients membersihkan klien tidak aktif
func (m *MonitoringManager) CleanupInactiveClients(inactiveTimeout time.Duration) int {
	m.hub.mu.RLock()
	var inactiveClients []*Client
	cutoff := time.Now().Add(-inactiveTimeout)

	for client := range m.hub.clients {
		if client.lastPing.Before(cutoff) {
			inactiveClients = append(inactiveClients, client)
		}
	}
	m.hub.mu.RUnlock()

	// Putuskan koneksi klien tidak aktif
	for _, client := range inactiveClients {
		m.hub.logActivity("cleanup_disconnect", client.ID,
			fmt.Sprintf("Inactive for %v", time.Since(client.lastPing)))
		client.cancel()
		client.Conn.Close()
	}

	return len(inactiveClients)
}

// DisconnectClient memutuskan koneksi klien tertentu
func (m *MonitoringManager) DisconnectClient(clientID string) bool {
	m.hub.mu.RLock()
	client, exists := m.hub.clientsByID[clientID]
	m.hub.mu.RUnlock()

	if !exists {
		return false
	}

	// Log aktivitas
	m.hub.logActivity("force_disconnect", clientID, "Client disconnected by admin")

	// Batalkan context dan tutup koneksi
	client.cancel()
	client.Conn.Close()

	return true
}
