package logging

import (
	"context"
	"log/slog"
)

type contextFieldsKey struct{}

// ContextFields contains request metadata attached to logs emitted with a
// request context.
type ContextFields struct {
	RequestID    string
	TraceID      string
	SpanID       string
	ParentSpanID string
	DeviceID     string
	ClientIP     string
	HTTPMethod   string
	HTTPPath     string
}

func WithContextFields(ctx context.Context, fields *ContextFields) context.Context {
	return context.WithValue(ctx, contextFieldsKey{}, fields)
}

func ContextFieldsFrom(ctx context.Context) *ContextFields {
	if ctx == nil {
		return nil
	}
	fields, _ := ctx.Value(contextFieldsKey{}).(*ContextFields)
	return fields
}

type contextHandler struct{ handler slog.Handler }

func (h *contextHandler) Enabled(ctx context.Context, level slog.Level) bool {
	return h.handler.Enabled(ctx, level)
}

func (h *contextHandler) Handle(ctx context.Context, record slog.Record) error {
	if fields := ContextFieldsFrom(ctx); fields != nil {
		record.AddAttrs(
			slog.String("request_id", fields.RequestID),
			slog.String("trace_id", fields.TraceID),
			slog.String("device_id", fields.DeviceID),
			slog.String("client_ip", fields.ClientIP),
			slog.String("http_method", fields.HTTPMethod),
			slog.String("http_path", fields.HTTPPath),
		)
		if fields.SpanID != "" {
			record.AddAttrs(slog.String("span_id", fields.SpanID))
		}
		if fields.ParentSpanID != "" {
			record.AddAttrs(slog.String("parent_span_id", fields.ParentSpanID))
		}
	}
	return h.handler.Handle(ctx, record)
}

func (h *contextHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
	return &contextHandler{handler: h.handler.WithAttrs(attrs)}
}

func (h *contextHandler) WithGroup(name string) slog.Handler {
	return &contextHandler{handler: h.handler.WithGroup(name)}
}
