package sentry

import (
	"sync"
)

// maxSpans limits the number of recorded spans per transaction. The limit is
// meant to bound memory usage and prevent too large transaction events that
// would be rejected by Sentry.
const maxSpans = 1000

// A spanRecorder stores a span tree that makes up a transaction. Safe for
// concurrent use. It is okay to add child spans from multiple goroutines.
type spanRecorder struct {
	mu           sync.Mutex
	spans        []*Span
	overflowOnce sync.Once
}

// record stores a span. The first stored span is assumed to be the root of a
// span tree.
func (r *spanRecorder) record(s *Span) {
	r.mu.Lock()
	defer r.mu.Unlock()
	if len(r.spans) >= maxSpans {
		r.overflowOnce.Do(func() {
			root := r.spans[0]
			Logger.Printf("Too many spans: dropping spans from transaction with TraceID=%s SpanID=%s limit=%d",
				root.TraceID, root.SpanID, maxSpans)
		})
		// TODO(tracing): mark the transaction event in some way to
		// communicate that spans were dropped.
		return
	}
	r.spans = append(r.spans, s)
}

// root returns the first recorded span. Returns nil if none have been recorded.
func (r *spanRecorder) root() *Span {
	r.mu.Lock()
	defer r.mu.Unlock()
	if len(r.spans) == 0 {
		return nil
	}
	return r.spans[0]
}

// children returns a list of all recorded spans, except the root. Returns nil
// if there are no children.
func (r *spanRecorder) children() []*Span {
	r.mu.Lock()
	defer r.mu.Unlock()
	if len(r.spans) < 2 {
		return nil
	}
	return r.spans[1:]
}
