package api

import (
	"context"
	"io"
	"math"
	"net/http"
	"sync"
	"time"
)

// svcHealth is the health of one upstream service, as shown on the panel.
type svcHealth struct {
	Status     string `json:"status"` // "ok" | "down"
	HTTPStatus int    `json:"httpStatus,omitempty"`
	URL        string `json:"url,omitempty"`
	// Fractional, and a pointer so an absent measurement (this process) stays
	// distinguishable from a genuinely fast one: neighbours sharing a Docker
	// network answer in well under a millisecond.
	LatencyMs *float64 `json:"latencyMs,omitempty"`
	Error     string   `json:"error,omitempty"`
}

// latencyMs is elapsed time in milliseconds, kept to one decimal.
func latencyMs(d time.Duration) *float64 {
	ms := math.Round(float64(d.Microseconds())/100) / 10
	return &ms
}

// healthClient is a short-timeout client for probing upstreams so a hung
// dependency can't stall the status endpoint.
var healthClient = &http.Client{Timeout: 4 * time.Second}

// probe does a GET against url and classifies the result.
func probe(ctx context.Context, url string) svcHealth {
	start := time.Now()
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
	if err != nil {
		return svcHealth{Status: "down", URL: url, Error: err.Error()}
	}
	resp, err := healthClient.Do(req)
	lat := latencyMs(time.Since(start))
	if err != nil {
		return svcHealth{Status: "down", URL: url, LatencyMs: lat, Error: err.Error()}
	}
	defer resp.Body.Close()
	_, _ = io.Copy(io.Discard, resp.Body)
	status := "ok"
	if resp.StatusCode >= 400 {
		status = "down"
	}
	return svcHealth{Status: status, LatencyMs: lat, HTTPStatus: resp.StatusCode, URL: url}
}

// GET /api/status — aggregate health of the API Server and its neighbours
// (PocketBase and the Web App), probed server-side. The panel polls this so the
// browser never has to reach PocketBase or the Web App directly.
func (s *Server) handleStatus(w http.ResponseWriter, r *http.Request) {
	var pbHealth, web svcHealth
	var wg sync.WaitGroup
	wg.Add(2)
	go func() { defer wg.Done(); pbHealth = probe(r.Context(), s.pb.BaseURL()+"/api/health") }()
	go func() { defer wg.Done(); web = probe(r.Context(), s.webAppURL()+"/healthz") }()
	wg.Wait()

	writeJSON(w, http.StatusOK, map[string]any{
		"apiServer":  map[string]any{"status": "ok"},
		"pocketBase": pbHealth,
		"webApp":     web,
	})
}
