package redisconn

import (
	"errors"
	"fmt"

	"github.com/redis/go-redis/extra/redisotel/v9"
	"github.com/redis/go-redis/v9"
	"go.opentelemetry.io/otel/metric"
	"go.opentelemetry.io/otel/trace"
)

// Option configures the OpenTelemetry providers attached to a client by
// Instrument. Unset providers fall back to the OpenTelemetry global
// providers (matching `redisotel` defaults).
type Option func(*options)

type options struct {
	tracerProvider trace.TracerProvider
	meterProvider  metric.MeterProvider
}

// WithTracerProvider sets the tracer provider used for Redis command spans.
func WithTracerProvider(tp trace.TracerProvider) Option {
	return func(o *options) {
		o.tracerProvider = tp
	}
}

// WithMeterProvider sets the meter provider used for Redis client metrics.
func WithMeterProvider(mp metric.MeterProvider) Option {
	return func(o *options) {
		o.meterProvider = mp
	}
}

func evalOptions(opts []Option) options {
	var resolved options
	for _, opt := range opts {
		if opt == nil {
			continue
		}
		opt(&resolved)
	}
	return resolved
}

// Instrument attaches Redis tracing and metrics to client. Tracing is
// configured with `WithDBStatement(false)` so that only the command name is
// captured, matching the existing instrumentation in the user, lobby, and
// notification services.
func Instrument(client *redis.Client, opts ...Option) error {
	if client == nil {
		return errors.New("instrument redis client: nil client")
	}

	resolved := evalOptions(opts)

	traceOpts := []redisotel.TracingOption{redisotel.WithDBStatement(false)}
	if resolved.tracerProvider != nil {
		traceOpts = append(traceOpts, redisotel.WithTracerProvider(resolved.tracerProvider))
	}
	if err := redisotel.InstrumentTracing(client, traceOpts...); err != nil {
		return fmt.Errorf("instrument redis client tracing: %w", err)
	}

	metricOpts := []redisotel.MetricsOption{}
	if resolved.meterProvider != nil {
		metricOpts = append(metricOpts, redisotel.WithMeterProvider(resolved.meterProvider))
	}
	if err := redisotel.InstrumentMetrics(client, metricOpts...); err != nil {
		return fmt.Errorf("instrument redis client metrics: %w", err)
	}
	return nil
}
