package slogadapter

import (
	"context"
	"log/slog"

	"github.com/grafana/grafana/pkg/infra/log"
)

var _ slog.Handler = &slogHandler{}

type slogHandler struct {
	log.Logger
}

func init() {
	// Lots of New's here: Default = slog.Logger <- slog.Handler <- infra/log.Logger
	slog.SetDefault(slog.New(New(log.New())))
}

// Provide is a helper method to be used with Wire, however most services should use slog.Default()
func Provide() slog.Handler { return New(log.New()) }

// NewSLogHandler returns a new slog.Handler that logs to the given log.Logger.
func New(logger log.Logger) *slogHandler {
	return &slogHandler{logger}
}

// Enabled implements slog.Handler.Enabled.
func (h *slogHandler) Enabled(_ context.Context, _ slog.Level) bool {
	return true
}

// Handle implements slog.Handler.Handle.
func (h *slogHandler) Handle(ctx context.Context, r slog.Record) error {
	attrs := make([]any, 0, 2*r.NumAttrs())
	fn := func(attr slog.Attr) bool {
		attrs = append(attrs, attr.Key, attr.Value)
		return true
	}
	r.Attrs(fn)
	attrs = append(attrs, log.FromContext(ctx)...)

	switch level := r.Level; {
	case level < slog.LevelInfo:
		h.Debug(r.Message, attrs...)
	case level < slog.LevelWarn:
		h.Info(r.Message, attrs...)
	case level < slog.LevelError:
		h.Warn(r.Message, attrs...)
	default:
		h.Error(r.Message, attrs...)
	}
	return nil
}

// WithAttrs implements slog.Handler.WithAttrs.
func (h *slogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
	out := make([]any, 0, 2*len(attrs))
	for _, attr := range attrs {
		out = append(out, attr.Key, attr.Value)
	}
	return New(h.Logger.New(out...))
}

// WithGroup implements slog.Handler.WithGroup.
func (h *slogHandler) WithGroup(name string) slog.Handler {
	if name == "" {
		return h
	}
	return New(h.Logger.New("group", name))
}
