// Package health provides a standalone HTTP server for health checks.
//
// This package implements a simple health check server that can be used
// to expose health status endpoints for monitoring and load balancing.
// It supports custom health check handlers and provides structured logging
// with graceful shutdown capabilities.
package health

import (
	"context"
	"log/slog"
	"net/http"
	"strconv"
	"time"

	"go.ntppool.org/common/logger"
	"golang.org/x/sync/errgroup"
)

// Server is a standalone HTTP server dedicated to health checks.
// It runs separately from the main application server to ensure health
// checks remain available even if the main server is experiencing issues.
//
// The server includes built-in timeouts, graceful shutdown, and structured
// logging for monitoring and debugging health check behavior.
type Server struct {
	log      *slog.Logger
	healthFn http.HandlerFunc
}

// NewServer creates a new health check server with the specified health handler.
// If healthFn is nil, a default handler that returns HTTP 200 "ok" is used.
func NewServer(healthFn http.HandlerFunc) *Server {
	if healthFn == nil {
		healthFn = basicHealth
	}
	srv := &Server{
		log:      logger.Setup(),
		healthFn: healthFn,
	}
	return srv
}

// SetLogger replaces the default logger with a custom one.
func (srv *Server) SetLogger(log *slog.Logger) {
	srv.log = log
}

// Listen starts the health server on the specified port and blocks until ctx is cancelled.
// The server exposes the health handler at "/__health" with graceful shutdown support.
func (srv *Server) Listen(ctx context.Context, port int) error {
	srv.log.Info("starting health listener", "port", port)

	serveMux := http.NewServeMux()

	serveMux.HandleFunc("/__health", srv.healthFn)

	hsrv := &http.Server{
		Addr:         ":" + strconv.Itoa(port),
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 20 * time.Second,
		IdleTimeout:  120 * time.Second,
		Handler:      serveMux,
	}

	g, ctx := errgroup.WithContext(ctx)

	g.Go(func() error {
		err := hsrv.ListenAndServe()
		if err != http.ErrServerClosed {
			srv.log.Warn("health check server done listening", "err", err)
			return err
		}
		return nil
	})

	<-ctx.Done()

	g.Go(func() error {
		shCtx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
		defer cancel()
		if err := hsrv.Shutdown(shCtx); err != nil {
			srv.log.Error("health check server shutdown failed", "err", err)
			return err
		}
		return nil
	})

	return g.Wait()
}

// HealthCheckListener runs a simple HTTP server on the specified port for health check probes.
func HealthCheckListener(ctx context.Context, port int, log *slog.Logger) error {
	srv := NewServer(nil)
	srv.SetLogger(log)
	return srv.Listen(ctx, port)
}

func basicHealth(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(200)
	w.Write([]byte("ok"))
}