package otelx

import (
	"bytes"
	"context"
	"log/slog"
	"strings"
	"testing"

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

func newLogger(buf *bytes.Buffer) *slog.Logger {
	return slog.New(traceHandler{slog.NewJSONHandler(buf, &slog.HandlerOptions{Level: slog.LevelInfo})})
}

func ctxWithSpan(traceHex, spanHex string) context.Context {
	tid, _ := trace.TraceIDFromHex(traceHex)
	sid, _ := trace.SpanIDFromHex(spanHex)
	sc := trace.NewSpanContext(trace.SpanContextConfig{TraceID: tid, SpanID: sid})
	return trace.ContextWithSpanContext(context.Background(), sc)
}

func TestTraceHandlerInjectsIDs(t *testing.T) {
	var buf bytes.Buffer
	logger := newLogger(&buf)
	ctx := ctxWithSpan("0123456789abcdef0123456789abcdef", "0123456789abcdef")

	logger.InfoContext(ctx, "hello")

	out := buf.String()
	if !strings.Contains(out, `"trace_id":"0123456789abcdef0123456789abcdef"`) {
		t.Fatalf("expected trace_id in log, got: %s", out)
	}
	if !strings.Contains(out, `"span_id":"0123456789abcdef"`) {
		t.Fatalf("expected span_id in log, got: %s", out)
	}
}

func TestTraceHandlerNoSpanNoIDs(t *testing.T) {
	var buf bytes.Buffer
	logger := newLogger(&buf)

	logger.InfoContext(context.Background(), "hello")

	if strings.Contains(buf.String(), "trace_id") {
		t.Fatalf("did not expect trace_id without a span, got: %s", buf.String())
	}
}

func TestTraceHandlerPreservesWithAttrs(t *testing.T) {
	var buf bytes.Buffer
	logger := newLogger(&buf).With("service", "test-svc")
	ctx := ctxWithSpan("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbb")

	logger.InfoContext(ctx, "hello")

	out := buf.String()
	// With(...) 后 trace 注入仍生效（WithAttrs 返回包装后的 handler）。
	if !strings.Contains(out, `"service":"test-svc"`) || !strings.Contains(out, "trace_id") {
		t.Fatalf("expected both service and trace_id after With(), got: %s", out)
	}
}

func TestParseLevel(t *testing.T) {
	cases := map[string]slog.Level{
		"debug": slog.LevelDebug, "info": slog.LevelInfo, "warn": slog.LevelWarn,
		"error": slog.LevelError, "": slog.LevelInfo, "bogus": slog.LevelInfo,
	}
	for in, want := range cases {
		if got := parseLevel(in); got != want {
			t.Errorf("parseLevel(%q)=%v want %v", in, got, want)
		}
	}
}
