package server

import (
	"context"
	"io/fs"
	"log/slog"
	"net/http"
	"time"

	"github.com/uopi/uopi/internal/api"
	"github.com/uopi/uopi/internal/broker"
	"github.com/uopi/uopi/internal/datasource/synthetic"
	"github.com/uopi/uopi/internal/metrics"
	"github.com/uopi/uopi/internal/storage"
)

const apiPrefix = "/api/v1"

type Server struct {
	httpServer *http.Server
	log        *slog.Logger
}

// New creates the HTTP server, registers all routes, and returns a ready-to-start Server.
// synth may be nil if the synthetic data source is not enabled.
func New(addr string, webFS fs.FS, brk *broker.Broker, synth *synthetic.Synthetic, store *storage.Store, channelFinderURL string, log *slog.Logger) *Server {
	mux := http.NewServeMux()

	// Health check
	mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write([]byte("ok"))
	})

	// WebSocket endpoint
	mux.Handle("/ws", &wsHandler{broker: brk, log: log})

	// Prometheus-format metrics
	mux.HandleFunc("/metrics", metrics.Handler(brk.ActiveSubscriptions))

	// REST API
	api.New(brk, synth, store, channelFinderURL, log).Register(mux, apiPrefix)

	// Embedded frontend — must be last (catch-all)
	mux.Handle("/", http.FileServerFS(webFS))

	return &Server{
		httpServer: &http.Server{
			Addr:         addr,
			Handler:      mux,
			ReadTimeout:  10 * time.Second,
			WriteTimeout: 30 * time.Second,
			IdleTimeout:  120 * time.Second,
		},
		log: log,
	}
}

// Start listens and serves until ctx is cancelled.
func (s *Server) Start(ctx context.Context) error {
	s.log.Info("listening", "addr", s.httpServer.Addr)

	errCh := make(chan error, 1)
	go func() {
		if err := s.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			errCh <- err
		}
	}()

	select {
	case err := <-errCh:
		return err
	case <-ctx.Done():
		shutCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		s.log.Info("shutting down")
		return s.httpServer.Shutdown(shutCtx)
	}
}
