package server

import (
	"context"
	"encoding/json"
	"net/http"
	"time"

	"github.com/coder/websocket"

	"github.com/operator/command-center/internal/eventbus"
)

// handleWebSocket is the Phase 3 minimal subscriber endpoint. The client
// connects, automatically subscribes to TopicTorrents, and receives JSON
// events as they're published. Phase 4 extends this with a typed
// subscribe/unsubscribe protocol; for Phase 3 the single-topic shape is
// sufficient for the dashboard's live-update needs.
func (s *Server) handleWebSocket(w http.ResponseWriter, r *http.Request) {
	if s.bus == nil {
		http.Error(w, "eventbus not configured", http.StatusServiceUnavailable)
		return
	}
	c, err := websocket.Accept(w, r, &websocket.AcceptOptions{
		// CompressionDisabled keeps the surface small for Phase 3; per-message
		// deflate can land alongside multi-topic subscription in Phase 4.
		CompressionMode: websocket.CompressionDisabled,
		OriginPatterns:  []string{"*"}, // Tailscale-only network model makes this safe
	})
	if err != nil {
		s.logger.Warn().Err(err).Msg("ws upgrade failed")
		return
	}
	ctx, cancel := context.WithCancel(r.Context())
	defer cancel()
	defer c.Close(websocket.StatusNormalClosure, "")

	sub := s.bus.Subscribe(eventbus.TopicTorrents)
	defer sub.Close()

	// Read pump (drains pings + a stub topic-control frame): if the client
	// closes, we exit. Phase 4 will implement subscribe/unsubscribe here.
	go func() {
		for {
			_, _, err := c.Read(ctx)
			if err != nil {
				cancel()
				return
			}
		}
	}()

	// Write pump.
	for {
		select {
		case <-ctx.Done():
			return
		case ev, ok := <-sub.Chan():
			if !ok {
				return
			}
			payload, err := json.Marshal(map[string]any{
				"topic":   ev.Topic,
				"payload": ev.Payload,
			})
			if err != nil {
				continue
			}
			writeCtx, wcancel := context.WithTimeout(ctx, 10*time.Second)
			err = c.Write(writeCtx, websocket.MessageText, payload)
			wcancel()
			if err != nil {
				return
			}
		}
	}
}
