package logging

import (
	"context"
	"log/slog"
)

// RedactingHandler wraps an slog.Handler to redact sensitive data.
type RedactingHandler struct {
	inner slog.Handler
}

// NewRedactingHandler creates a handler that redacts sensitive values.
func NewRedactingHandler(inner slog.Handler) *RedactingHandler {
	return &RedactingHandler{inner: inner}
}

// Enabled implements slog.Handler.
func (h *RedactingHandler) Enabled(ctx context.Context, level slog.Level) bool {
	return h.inner.Enabled(ctx, level)
}

// Handle implements slog.Handler.
func (h *RedactingHandler) Handle(ctx context.Context, r slog.Record) error {
	// Clone the record with redacted attributes
	newRecord := slog.NewRecord(r.Time, r.Level, r.Message, r.PC)
	r.Attrs(func(a slog.Attr) bool {
		newRecord.AddAttrs(h.redactAttr(a))
		return true
	})
	return h.inner.Handle(ctx, newRecord)
}

// WithAttrs implements slog.Handler.
func (h *RedactingHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
	redacted := make([]slog.Attr, len(attrs))
	for i, a := range attrs {
		redacted[i] = h.redactAttr(a)
	}
	return &RedactingHandler{inner: h.inner.WithAttrs(redacted)}
}

// WithGroup implements slog.Handler.
func (h *RedactingHandler) WithGroup(name string) slog.Handler {
	return &RedactingHandler{inner: h.inner.WithGroup(name)}
}

// redactAttr redacts an attribute if its key or value is sensitive.
func (h *RedactingHandler) redactAttr(a slog.Attr) slog.Attr {
	// Check if the field name indicates sensitive data
	if IsSensitiveField(a.Key) {
		return slog.String(a.Key, RedactedValue)
	}

	// Check the value based on its kind
	switch a.Value.Kind() {
	case slog.KindString:
		s := a.Value.String()
		if ContainsSensitiveData(s) {
			return slog.String(a.Key, RedactedValue)
		}
	case slog.KindGroup:
		// Recursively redact group attributes
		attrs := a.Value.Group()
		redacted := make([]slog.Attr, len(attrs))
		for i, attr := range attrs {
			redacted[i] = h.redactAttr(attr)
		}
		return slog.Attr{Key: a.Key, Value: slog.GroupValue(redacted...)}
	}

	return a
}
