package main

import (
	"context"
	"sync"
)

// The query source that claimed a turn was visible in the daemon log and
// nowhere else (V-539). A QA step reading /chat could see a wrong answer but
// not tell a wrong answer from a wrongly ordered chain: "почему небо голубое"
// answered badly reads the same whether search claimed it, the ZIM did, or the
// resident model answered from memory.
//
// It rides the context rather than a return value because handleText answers
// every reach through one string, and threading a second value through the
// whole action dispatch would change a signature the mic, telegram and the web
// all share. The sink is per turn, created by the caller that wants to read it;
// a turn with no sink notes nothing, which is what the mic path does.
type querySourceKey struct{}

// querySourceSink holds the name of the source that claimed one turn. The mutex
// is there because a query source may fan out to goroutines of its own, not
// because two turns share a sink.
type querySourceSink struct {
	mu   sync.Mutex
	name string
}

func (s *querySourceSink) note(name string) {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.name = name
}

// Name is the source that claimed, or empty when nothing did or the turn was
// not a query at all.
func (s *querySourceSink) Name() string {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.name
}

// withQuerySourceSink returns a context that collects the claiming source, and
// the sink to read after the turn has answered.
func withQuerySourceSink(ctx context.Context) (context.Context, *querySourceSink) {
	sink := &querySourceSink{}
	return context.WithValue(ctx, querySourceKey{}, sink), sink
}

// noteQuerySource records which source claimed the turn. It is a no-op when the
// caller did not ask for one.
func noteQuerySource(ctx context.Context, name string) {
	if sink, ok := ctx.Value(querySourceKey{}).(*querySourceSink); ok {
		sink.note(name)
	}
}
