// Package observability provides slog-based structured logging for push-gateway.
//
// The logger emits JSON in production and a human-readable console format in
// development (DEV_MODE=true). Trace correlation is handled by attaching a
// trace_id attribute from the OpenTelemetry span context when present.
package observability

import (
	"context"
	"log/slog"
	"os"
	"time"

	"go.opentelemetry.io/otel/trace"
)

// ctxKey is an unexported type so context values cannot collide with callers.
type ctxKey struct{}

// logger is the process-wide *slog.Logger initialized by InitLogger.
var logger *slog.Logger

// InitLogger configures the global slog logger. In DevMode a console handler
// with colored levels is used; otherwise a JSON handler at Info level is used.
// The logger is tagged with service=push-gateway on every emitted record.
func InitLogger(devMode bool) *slog.Logger {
	var handler slog.Handler
	opts := &slog.HandlerOptions{
		Level: slog.LevelInfo,
	}
	if devMode {
		opts.Level = slog.LevelDebug
		handler = slog.NewTextHandler(os.Stdout, opts)
	} else {
		handler = slog.NewJSONHandler(os.Stdout, opts)
	}
	logger = slog.New(handler).With("service", "push-gateway")
	slog.SetDefault(logger)
	return logger
}

// Logger returns the process-wide logger initialized by InitLogger. When Init
// has not been called a fallback JSON logger is returned so callers never
// receive a nil logger.
func Logger() *slog.Logger {
	if logger == nil {
		logger = slog.New(slog.NewJSONHandler(os.Stdout, nil)).With("service", "push-gateway")
	}
	return logger
}

// WithContext returns a copy of ctx carrying l so it can later be retrieved
// via FromContext.
func WithContext(ctx context.Context, l *slog.Logger) context.Context {
	return context.WithValue(ctx, ctxKey{}, l)
}

// FromContext returns the logger stored in ctx. When an OpenTelemetry span is
// active the returned logger is decorated with a trace_id field so log lines
// can be correlated to traces. If no logger was stored in ctx the
// process-wide logger is returned (never nil).
func FromContext(ctx context.Context) *slog.Logger {
	l, ok := ctx.Value(ctxKey{}).(*slog.Logger)
	if !ok || l == nil {
		l = Logger()
	}
	if sc := trace.SpanContextFromContext(ctx); sc.HasTraceID() {
		return l.With("trace_id", sc.TraceID().String())
	}
	return l
}

// Now returns the current UTC time in RFC3339 format. Convenience helper for
// log field construction.
func Now() string {
	return time.Now().UTC().Format(time.RFC3339)
}
