package httpd

import (
	"fmt"
	"log/slog"
	"net/http"
	"runtime/debug"
	"strings"
	"time"

	"github.com/go-chi/chi/v5/middleware"

	"github.com/aoagents/agent-orchestrator/backend/internal/httpd/envelope"
	"github.com/aoagents/agent-orchestrator/backend/internal/ports"
	"github.com/aoagents/agent-orchestrator/backend/internal/telemetrymeta"
)

func recoverTelemetry(log *slog.Logger, sink ports.EventSink) func(http.Handler) http.Handler {
	log = loggerOrDefault(log)
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			defer func() {
				if rec := recover(); rec != nil {
					stack := string(debug.Stack())
					log.Error("http handler panic",
						"id", middleware.GetReqID(r.Context()),
						"method", r.Method,
						"path", r.URL.Path,
						"panic", fmt.Sprint(rec),
						"stack", stack,
					)
					if sink != nil {
						path := telemetrymeta.RoutePattern(r)
						panicKind := telemetrymeta.PanicKind(rec)
						sink.Emit(r.Context(), ports.TelemetryEvent{
							Name:       "ao.daemon.panic",
							Source:     "http",
							OccurredAt: time.Now().UTC(),
							Level:      ports.TelemetryLevelError,
							RequestID:  middleware.GetReqID(r.Context()),
							Payload: map[string]any{
								"component":         "httpd",
								"operation":         "http_request_panic",
								"method":            r.Method,
								"path":              path,
								"panic_kind":        panicKind,
								"stack_fingerprint": telemetrymeta.Fingerprint("httpd", "http_request_panic", path, panicKind, stack),
								"fingerprint":       telemetrymeta.Fingerprint("httpd", "http_request_panic", r.Method, path, panicKind),
							},
						})
					}
					writeRecoveredError(w, r)
				}
			}()
			next.ServeHTTP(w, r)
		})
	}
}

func writeRecoveredError(w http.ResponseWriter, r *http.Request) {
	if strings.HasPrefix(r.URL.Path, "/api/") {
		envelope.WriteAPIError(w, r, http.StatusInternalServerError, "internal_error", "INTERNAL_ERROR", "Internal server error", nil)
		return
	}
	envelope.WriteJSON(w, http.StatusInternalServerError, map[string]any{
		"status": "error",
	})
}
