package restapi

import (
	"time"

	"galaxy/gateway/internal/logging"
	"galaxy/gateway/internal/telemetry"

	"github.com/gin-gonic/gin"
	"go.opentelemetry.io/otel/attribute"
	"go.uber.org/zap"
)

func withPublicObservability(logger *zap.Logger, metrics *telemetry.Runtime) gin.HandlerFunc {
	if logger == nil {
		logger = zap.NewNop()
	}

	return func(c *gin.Context) {
		start := time.Now()
		c.Next()

		statusCode := c.Writer.Status()
		route := c.FullPath()
		if route == "" {
			route = c.Request.URL.Path
		}

		class, ok := PublicRouteClassFromContext(c.Request.Context())
		if !ok {
			class = PublicRouteClassPublicMisc
		}

		errorCode, _ := c.Get(publicErrorCodeContextKey)
		errorCodeValue, _ := errorCode.(string)

		outcome := telemetry.OutcomeFromPublicErrorCode(statusCode, errorCodeValue)
		rejectReason := telemetry.RejectReason(outcome)
		duration := time.Since(start)

		attrs := []attribute.KeyValue{
			attribute.String("route_class", string(class)),
			attribute.String("route", route),
			attribute.String("method", c.Request.Method),
			attribute.String("edge_outcome", string(outcome)),
		}
		if rejectReason != "" {
			attrs = append(attrs, attribute.String("reject_reason", rejectReason))
		}
		metrics.RecordPublicRequest(c.Request.Context(), attrs, duration)

		fields := []zap.Field{
			zap.String("component", "public_http"),
			zap.String("transport", "http"),
			zap.String("route", route),
			zap.String("route_class", string(class)),
			zap.String("method", c.Request.Method),
			zap.Int("status_code", statusCode),
			zap.Float64("duration_ms", float64(duration.Microseconds())/1000),
			zap.String("edge_outcome", string(outcome)),
		}
		if rejectReason != "" {
			fields = append(fields, zap.String("reject_reason", rejectReason))
		}
		fields = append(fields, logging.TraceFieldsFromContext(c.Request.Context())...)

		switch outcome {
		case telemetry.EdgeOutcomeSuccess:
			logger.Info("public request completed", fields...)
		case telemetry.EdgeOutcomeBackendUnavailable, telemetry.EdgeOutcomeInternalError:
			logger.Error("public request failed", fields...)
		default:
			logger.Warn("public request rejected", fields...)
		}
	}
}
