package healthz

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"sync/atomic"
	"time"
)

// Server is the public interface of the minimal HTTP health check server.
type Server interface {
	// Addr returns the bound network address (e.g. "127.0.0.1:8080").
	Addr() string
	// Start begins serving HTTP requests. It blocks until Stop is called.
	Start() error
	// Stop initiates graceful shutdown. After Stop is called the /healthz
	// endpoint returns 503 while in-flight requests complete.
	Stop(ctx context.Context) error
}

// server is the private implementation of Server.
type server struct {
	listener     net.Listener
	httpServer   *http.Server
	shuttingDown atomic.Bool
}

// New creates a health check server listening on the given port.
// Passing port 0 binds to an available ephemeral port.
func New(port int) (Server, error) {
	listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
	if err != nil {
		return nil, fmt.Errorf("failed to create listener: %w", err)
	}

	s := &server{listener: listener}
	s.httpServer = &http.Server{
		Handler:           http.HandlerFunc(s.handleHealthz),
		ReadHeaderTimeout: 5 * time.Second,
	}
	return s, nil
}

// Addr returns the bound network address (e.g. "127.0.0.1:8080").
func (s *server) Addr() string {
	if s.listener == nil {
		return ""
	}
	return s.listener.Addr().String()
}

// Start begins serving HTTP requests. It blocks until Stop is called.
func (s *server) Start() error {
	return s.httpServer.Serve(s.listener)
}

// Stop initiates graceful shutdown. After Stop is called the /healthz
// endpoint returns 503 while in-flight requests complete.
func (s *server) Stop(ctx context.Context) error {
	s.shuttingDown.Store(true)
	// Grace period so that health checks can observe the 503 state
	// before the listener is closed.
	time.Sleep(100 * time.Millisecond)
	return s.httpServer.Shutdown(ctx)
}

func (s *server) handleHealthz(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != "/healthz" {
		http.NotFound(w, r)
		return
	}
	if s.shuttingDown.Load() {
		w.WriteHeader(http.StatusServiceUnavailable)
		return
	}
	w.WriteHeader(http.StatusOK)
}
