package handler

import (
	"encoding/json"
	"net/http"
	"sync"

	"github.com/gorilla/websocket"

	"git.yeij.top/AskaEth/Cyrene/platform-bridge/internal/logging"
)

var wsUpgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool { return true },
}

// LogWSHub broadcasts log entries to connected WebSocket clients.
type LogWSHub struct {
	mu      sync.Mutex
	clients map[*websocket.Conn]chan logging.LogEntry
}

// NewLogWSHub creates a LogWSHub and subscribes to the logger.
func NewLogWSHub(logger *logging.Logger) *LogWSHub {
	h := &LogWSHub{
		clients: make(map[*websocket.Conn]chan logging.LogEntry),
	}
	logger.OnLog(func(entry logging.LogEntry) {
		h.broadcast(entry)
	})
	return h
}

func (h *LogWSHub) broadcast(entry logging.LogEntry) {
	h.mu.Lock()
	defer h.mu.Unlock()
	for _, ch := range h.clients {
		select {
		case ch <- entry:
		default:
		}
	}
}

// ServeWS handles WebSocket upgrade and streams log entries to the client.
func (h *LogWSHub) ServeWS(w http.ResponseWriter, r *http.Request) {
	conn, err := wsUpgrader.Upgrade(w, r, nil)
	if err != nil {
		return
	}

	ch := make(chan logging.LogEntry, 64)
	h.mu.Lock()
	h.clients[conn] = ch
	h.mu.Unlock()

	// Write goroutine: drains ch until it is closed.
	done := make(chan struct{})
	go func() {
		defer close(done)
		for entry := range ch {
			data, _ := json.Marshal(entry)
			if err := conn.WriteMessage(websocket.TextMessage, data); err != nil {
				return
			}
		}
	}()

	// Read goroutine: detect client disconnect.
	// (websocket requires a reader to detect close frames.)
	go func() {
		for {
			if _, _, err := conn.ReadMessage(); err != nil {
				break
			}
		}
		// Client disconnected — stop broadcasting, close channel.
		h.mu.Lock()
		delete(h.clients, conn)
		h.mu.Unlock()
		close(ch)
	}()

	<-done
	conn.Close()
}
