package telemetry

import (
	"context"
	"time"
)

// telemetryOperationCtxKey represents the key used for setting the telemetry operation value in context.Context,
// used for setting a SampleReporter's operation
var telemetryOperationCtxKey = new(int)

// telemetryTimestampCtxKey represents the key used for setting the telemetry timestamp value in context.Context,
// used for setting a SampleReporter's timestamp and ensuring that all samples for the operation are grouped into
// the same time period so that metric math works out correctly.
var telemetryTimestampCtxKey = new(int)

// ContextWithOperationName allows the string-representation of the operation to be stored in the context for later use
// by telemetry tools like SampleReporters
func ContextWithOperationName(ctx context.Context, operation string) context.Context {
	return context.WithValue(ctx, telemetryOperationCtxKey, operation)
}

// ContextWithTimestamp allows a timestamp to be stored in the context for later use
// by telemetry tools like SampleReporters
func ContextWithTimestamp(ctx context.Context, timestamp time.Time) context.Context {
	return context.WithValue(ctx, telemetryTimestampCtxKey, timestamp)
}

// SampleReporterWithContext takes a SampleReporter and a Context, and returns a SampleReporter that uses information
// (like the operation and timestamp), from the provided Context
func SampleReporterWithContext(s SampleReporter, ctx context.Context) SampleReporter {
	s.OperationName = operationFrom(ctx)
	s.Timestamp = timestampFrom(ctx)
	return s
}

// operationFrom obtains the operation from the context or a MetricValueUnknownOperation string otherwise
func operationFrom(ctx context.Context) string {
	existing := ctx.Value(telemetryOperationCtxKey)
	if existing == nil {
		return MetricValueUnknownOperation
	}
	return existing.(string)
}

// timestampFrom obtains the timestamp from the context or the zero value otherwise
func timestampFrom(ctx context.Context) time.Time {
	existing := ctx.Value(telemetryTimestampCtxKey)
	if existing == nil {
		return time.Time{}
	}
	return existing.(time.Time)
}
