package tokenanalysis

import (
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"regexp"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/tidbcloud/AgentTeam/desktop/internal/metrics"
)

const maxReportLedger = 512

type contextBlockAggregate struct {
	summary BlockSummary
	runs    map[string]struct{}
	target  *uint64
}

type contextRunTotals struct {
	compiled     uint64
	controllable uint64
	exposure     uint64
	required     uint64
}

type contextAggregation struct {
	blocks        map[string]*contextBlockAggregate
	totalsByRun   map[string]contextRunTotals
	exposureByRun map[string]exposureRunCoverage
	launches      map[string]struct{}
	compiled      uint64
	controllable  uint64
	required      uint64
	partition     contextPartitionTotals
}

type exposureRunCoverage struct {
	observations        uint64
	complete            uint64
	expectedInvocations uint64
	identityUnsupported bool
}

type contextPartitionTotals struct {
	agentteamOwned         uint64
	workspaceConfiguration uint64
	userTaskPayload        uint64
	unknown                uint64
}

type usageRunAggregate struct {
	summary               RunSummary
	invocations           map[string]struct{}
	usageWithInvocationID int
}

type detailedUsageAggregation struct {
	runs                      map[string]*usageRunAggregate
	rootInvocations           map[string]struct{}
	agentTotals               map[string]AgentSummary
	rootInput                 uint64
	cacheRead                 uint64
	output                    uint64
	rootUsage                 int
	rootUsageWithInvocationID int
}

var newRefKey = func() [32]byte {
	var key [32]byte
	_, _ = rand.Read(key[:])
	return key
}

// Agent is the minimal configured-Agent projection required by analysis.
type Agent struct {
	ID      string
	Name    string
	Runtime string
	Model   string
}

type Dependencies struct {
	Evidence          metrics.TokenAnalysisQuerier
	Usage             *metrics.ServerMessageCollector
	Agents            func() []Agent
	CollectionChanged func(bool)
	ActiveLaunches    func() []string
	BuildFingerprint  string
	Now               func() time.Time
}

// Service is the daemon-lifetime owner for collection state and report
// construction.
type Service struct {
	mu      sync.RWMutex
	enabled bool
	refKey  [32]byte
	deps    Dependencies
}

func New(deps Dependencies) *Service {
	if deps.Now == nil {
		deps.Now = time.Now
	}
	if deps.Agents == nil {
		deps.Agents = func() []Agent { return nil }
	}
	refKey := newRefKey()
	if refKey == ([32]byte{}) {
		refKey = sha256.Sum256([]byte(fmt.Sprintf("%d", deps.Now().UnixNano())))
	}
	metrics.SetDetailedTokenCollectionEnabled(false)
	return &Service{
		refKey: refKey,
		deps:   deps,
	}
}

func (service *Service) CollectionEnabled() bool {
	if service == nil {
		return false
	}
	service.mu.RLock()
	defer service.mu.RUnlock()
	return service.enabled
}

func (service *Service) SetCollectionEnabled(enabled bool) bool {
	if service == nil {
		return false
	}
	service.mu.Lock()
	changed := service.enabled != enabled
	service.enabled = enabled
	service.mu.Unlock()
	metrics.SetDetailedTokenCollectionEnabled(enabled)
	if changed && service.deps.CollectionChanged != nil {
		service.deps.CollectionChanged(enabled)
	}
	return changed
}

// Analyze builds a report. A Human command may auto-start future detailed
// collection; Agent reads, UI refreshes, and --stop never do.
func (service *Service) Analyze(request Request, human, stop bool, agentID string, autoStart bool) (Report, error) {
	if service == nil {
		return Report{}, fmt.Errorf("token analysis service is unavailable")
	}
	window, err := ParseWindow(request.Window)
	if err != nil {
		return Report{}, err
	}
	if err := validateExperiment(request); err != nil {
		return Report{}, err
	}
	autoEnabled := false
	if human && autoStart && !stop && !service.CollectionEnabled() {
		autoEnabled = service.SetCollectionEnabled(true)
	}
	snapshotAt := service.deps.Now().UTC()
	activeLaunches := service.activeLaunches()
	agents := service.visibleAgents(human, agentID)
	agentIDs := make([]string, 0, len(agents))
	for _, configured := range agents {
		agentIDs = append(agentIDs, configured.ID)
	}
	selectedAgentID := service.resolveAgentRef(request.AgentRef, agents)
	if strings.TrimSpace(request.AgentRef) != "" && selectedAgentID == "" {
		return Report{}, fmt.Errorf("agent_ref_not_found")
	}
	if selectedAgentID != "" {
		agentIDs = []string{selectedAgentID}
	}
	runID, runAgentID := service.resolveRunRef(request.RunRef, agentIDs, window, snapshotAt)
	if strings.TrimSpace(request.RunRef) != "" && runID == "" {
		return Report{}, fmt.Errorf("run_ref_not_found")
	}
	if runID != "" && runAgentID != "" {
		agentIDs = []string{runAgentID}
		for _, configured := range agents {
			if configured.ID == runAgentID {
				agents = []Agent{configured}
				break
			}
		}
	}
	scopeKind := "all_agents"
	if selectedAgentID != "" {
		scopeKind = "agent"
	}
	if runID != "" {
		scopeKind = "run"
	}
	evidence := metrics.TokenAnalysisEvidence{
		PromptRecords: []metrics.PromptCompileRecord{},
		Usage:         []metrics.UsageObservationRecord{},
	}
	if service.deps.Evidence != nil {
		evidence = service.deps.Evidence.QueryTokenAnalysisEvidence(metrics.TokenEvidenceQuery{
			Window:     window,
			AgentIDs:   agentIDs,
			LaunchID:   runID,
			SnapshotAt: snapshotAt.UnixMilli(),
		})
	}
	report := service.buildReport(
		window,
		agents,
		agentIDs,
		evidence,
		runID,
		scopeKind,
		snapshotAt,
		activeLaunches,
		human,
		request.Experiment,
	)
	report.Collection.AutoEnabled = autoEnabled
	if stop {
		service.SetCollectionEnabled(false)
		report.Collection.Enabled = false
		report.Collection.Stopped = true
	}
	report.Fingerprint = reportFingerprint(report)
	return report, nil
}

func (service *Service) buildReport(
	window metrics.TimeWindow,
	agents []Agent,
	selectedAgentIDs []string,
	evidence metrics.TokenAnalysisEvidence,
	runID string,
	scopeKind string,
	snapshotAt time.Time,
	activeLaunches []string,
	callerCanControl bool,
	experiment ExperimentMetadata,
) Report {
	runScoped := runID != ""
	enabled := service.CollectionEnabled()
	lowCardinality := metrics.AgentDashboardMetrics{}
	if service.deps.Usage != nil && !runScoped {
		lowCardinality = service.deps.Usage.QueryAgentsDashboardAt(selectedAgentIDs, window, snapshotAt)
	}
	report := Report{
		SchemaVersion: AnalysisSchemaVersion,
		GeneratedAt:   snapshotAt,
		SnapshotAt:    snapshotAt,
		Window:        window.String(),
		Scope:         AnalysisScope{Kind: scopeKind, RunState: "not_applicable"},
		Collection:    CollectionStatus{Enabled: enabled, CallerCanControl: callerCanControl},
		Agents:        []AgentSummary{},
		Runs:          []RunSummary{},
		Ledger:        []BlockSummary{},
		Opportunities: []Opportunity{},
		DataQuality: DataQuality{
			State:       StateAvailable,
			Scanned:     evidence.Scanned,
			Limit:       evidence.Limit,
			Truncated:   evidence.Truncated,
			Diagnostics: []string{},
		},
		Comparability: ComparabilityInfo{
			BuildFingerprint:       declaredFingerprint("build", service.deps.BuildFingerprint),
			CacheState:             normalizedExperimentValue(experiment.CacheState, "unknown"),
			Outcome:                normalizedExperimentValue(experiment.Outcome, "unknown"),
			ScopeFingerprint:       scopeFingerprint(window.String(), scopeKind, len(selectedAgentIDs)),
			CaseFingerprint:        declaredFingerprint("case", experiment.Case),
			WorkspaceFingerprint:   declaredFingerprint("workspace", experiment.Workspace),
			ToolFingerprint:        declaredFingerprint("tools", experiment.Tools),
			TaskPayloadFingerprint: taskPayloadFP(evidence.PromptRecords, runID),
			ChangedVariable:        strings.TrimSpace(experiment.ChangedVariable),
		},
	}
	activeRunRefs := make(map[string]struct{})
	for _, active := range activeLaunches {
		activeRunRefs[service.runRef(active)] = struct{}{}
	}
	if runScoped {
		report.Scope.RunState = "ended"
		if _, ok := activeRunRefs[service.runRef(runID)]; ok {
			report.Scope.RunState = "active"
		}
	}
	service.addAgentSummaries(&report, agents, lowCardinality)
	report.Summary.RuntimeInput = lowCardinality.TokenSummary.Input
	report.Summary.RuntimeCacheRead = lowCardinality.TokenSummary.Cached
	report.Summary.RuntimeOutput = lowCardinality.TokenSummary.Output
	report.Summary.UsageEvents = lowCardinality.TokenSummary.Count
	report.Summary.UnattributedInput = availableValue(lowCardinality.TokenSummary.Input)

	if !enabled {
		report.DataQuality.State = StateDisabled
		report.DataQuality.Diagnostics = append(report.DataQuality.Diagnostics, "attribution_disabled")
		if !callerCanControl {
			report.DataQuality.Diagnostics = append(report.DataQuality.Diagnostics, "collection_enable_requires_human_owner")
		}
	}
	if report.Summary.UsageEvents == 0 && len(evidence.Usage) == 0 {
		if report.DataQuality.State != StateDisabled {
			report.DataQuality.State = StateNoData
		}
		report.DataQuality.Diagnostics = append(report.DataQuality.Diagnostics, "no_usage")
	}
	if evidence.Truncated {
		report.DataQuality.State = StateLimited
		report.DataQuality.Diagnostics = append(report.DataQuality.Diagnostics, "truncated_evidence")
	}

	service.addDetailedUsage(&report, evidence.Usage, runScoped)
	service.addContext(&report, evidence)
	for index := range report.Runs {
		_, report.Runs[index].ActiveAtSnapshot = activeRunRefs[report.Runs[index].RunRef]
	}
	if runScoped && len(report.Runs) == 1 {
		report.Scope.RunStartedAt = report.Runs[0].StartedAt
	}
	if runScoped && report.Scope.RunStartedAt == nil {
		report.Scope.RunStartedAt = launchStartedAt(evidence)
	}
	return report
}

func (service *Service) addAgentSummaries(report *Report, agents []Agent, lowCardinality metrics.AgentDashboardMetrics) {
	agentByID := make(map[string]Agent, len(agents))
	for _, configured := range agents {
		agentByID[configured.ID] = configured
	}
	for _, row := range lowCardinality.Agents {
		configured := agentByID[row.AgentID]
		report.Agents = append(report.Agents, AgentSummary{
			AgentRef:    service.agentRef(row.AgentID),
			DisplayName: firstNonEmpty(configured.Name, row.AgentID),
			Runtime:     configured.Runtime,
			Model:       configured.Model,
			Input:       row.Input,
			CacheRead:   row.Cached,
			Output:      row.Output,
		})
	}
	observedAgents := make(map[string]struct{}, len(report.Agents))
	for _, summary := range report.Agents {
		observedAgents[summary.AgentRef] = struct{}{}
	}
	for _, configured := range agents {
		ref := service.agentRef(configured.ID)
		if _, ok := observedAgents[ref]; ok {
			continue
		}
		report.Agents = append(report.Agents, AgentSummary{
			AgentRef:    ref,
			DisplayName: firstNonEmpty(configured.Name, configured.ID),
			Runtime:     configured.Runtime,
			Model:       configured.Model,
		})
	}
	sort.Slice(report.Agents, func(i, j int) bool {
		if report.Agents[i].DisplayName != report.Agents[j].DisplayName {
			return report.Agents[i].DisplayName < report.Agents[j].DisplayName
		}
		return report.Agents[i].AgentRef < report.Agents[j].AgentRef
	})
}

func (service *Service) addDetailedUsage(report *Report, usage []metrics.UsageObservationRecord, runScoped bool) {
	if len(usage) == 0 {
		report.Summary.RootModelInvocations = MetricValue{State: StateNoData}
		report.Summary.RootAverageInputPerInvocation = MetricValue{State: StateNoData}
		report.Summary.RootRuntimeInput = MetricValue{State: detailedState(report)}
		report.Summary.UnattributedInput = availableValue(report.Summary.RuntimeInput)
		return
	}
	aggregate := service.aggregateDetailedUsage(usage)
	report.Summary.RootRuntimeInput = availableValue(aggregate.rootInput)
	if runScoped {
		report.Summary.RuntimeInput = aggregate.rootInput
		report.Summary.RuntimeCacheRead = aggregate.cacheRead
		report.Summary.RuntimeOutput = aggregate.output
		report.Summary.UsageEvents = uint64(len(usage))
		for index := range report.Agents {
			total := aggregate.agentTotals[report.Agents[index].AgentRef]
			report.Agents[index].Input = total.Input
			report.Agents[index].CacheRead = total.CacheRead
			report.Agents[index].Output = total.Output
		}
	}
	correlatedInput := aggregate.rootInput
	report.Summary.UnattributedInput = availableValue(saturatingSubtract(report.Summary.RuntimeInput, correlatedInput))
	if report.Summary.RuntimeInput != correlatedInput {
		if report.DataQuality.State == StateAvailable {
			report.DataQuality.State = StateLimited
		}
		report.DataQuality.Diagnostics = append(report.DataQuality.Diagnostics, "usage_correlation_incomplete")
	}
	switch {
	case aggregate.rootUsage == 0:
		report.Summary.RootModelInvocations = MetricValue{State: StateNoData}
		report.Summary.RootAverageInputPerInvocation = MetricValue{State: StateNoData}
	case aggregate.rootUsageWithInvocationID != aggregate.rootUsage:
		report.Summary.RootModelInvocations = MetricValue{State: StateUnsupported}
		report.Summary.RootAverageInputPerInvocation = MetricValue{State: StateUnsupported}
		report.DataQuality.Diagnostics = append(report.DataQuality.Diagnostics, "root_invocation_identity_unsupported")
	default:
		count := uint64(len(aggregate.rootInvocations))
		report.Summary.RootModelInvocations = availableValue(count)
		if count == 0 {
			report.Summary.RootAverageInputPerInvocation = MetricValue{State: StateNoData}
		} else {
			report.Summary.RootAverageInputPerInvocation = availableValue(aggregate.rootInput / count)
		}
	}
	for _, run := range aggregate.runs {
		if run.summary.UsageObservations > 0 &&
			run.usageWithInvocationID == int(run.summary.UsageObservations) {
			count := uint64(len(run.invocations))
			run.summary.ModelInvocations = availableValue(count)
			if count == 0 {
				run.summary.AverageInputPerInvocation = MetricValue{State: StateNoData}
			} else {
				run.summary.AverageInputPerInvocation = availableValue(run.summary.Input / count)
			}
		} else {
			run.summary.AverageInputPerInvocation = MetricValue{State: StateUnsupported}
		}
		report.Runs = append(report.Runs, run.summary)
	}
	sort.Slice(report.Runs, func(i, j int) bool {
		if report.Runs[i].Input != report.Runs[j].Input {
			return report.Runs[i].Input > report.Runs[j].Input
		}
		return report.Runs[i].RunRef < report.Runs[j].RunRef
	})
	if len(report.Runs) > 4096 {
		report.Runs = report.Runs[:4096]
		report.DataQuality.State = StateLimited
		report.DataQuality.Truncated = true
		report.DataQuality.Diagnostics = append(report.DataQuality.Diagnostics, "runs_truncated")
	}
}

func (service *Service) aggregateDetailedUsage(usage []metrics.UsageObservationRecord) detailedUsageAggregation {
	aggregate := detailedUsageAggregation{
		runs:            make(map[string]*usageRunAggregate),
		rootInvocations: make(map[string]struct{}),
		agentTotals:     make(map[string]AgentSummary),
	}
	for _, observation := range usage {
		agentRef := service.agentRef(observation.AgentID)
		agent := aggregate.agentTotals[agentRef]
		agent.AgentRef = agentRef
		agent.Input += observation.InputTokens
		agent.CacheRead += observation.CachedTokens
		agent.Output += observation.OutputTokens
		aggregate.agentTotals[agentRef] = agent
		aggregate.cacheRead += observation.CachedTokens
		aggregate.output += observation.OutputTokens

		run := aggregate.runs[observation.LaunchID]
		if run == nil {
			run = &usageRunAggregate{
				summary: RunSummary{
					RunRef:           service.runRef(observation.LaunchID),
					AgentRef:         agentRef,
					Kind:             "root",
					Runtime:          observation.Runtime,
					Model:            observation.Model,
					ModelInvocations: MetricValue{State: StateUnsupported},
				},
				invocations: make(map[string]struct{}),
			}
			aggregate.runs[observation.LaunchID] = run
		}
		if observation.LaunchStartedAt > 0 {
			startedAt := time.UnixMilli(observation.LaunchStartedAt).UTC()
			if run.summary.StartedAt == nil || startedAt.Before(*run.summary.StartedAt) {
				run.summary.StartedAt = &startedAt
			}
		}
		run.summary.Input += observation.InputTokens
		run.summary.CacheRead += observation.CachedTokens
		run.summary.Output += observation.OutputTokens
		run.summary.UsageObservations++

		aggregate.rootInput += observation.InputTokens
		aggregate.rootUsage++
		if id := strings.TrimSpace(observation.InvocationID); id != "" {
			key := observation.Runtime + "\x00" + observation.SessionID + "\x00" + id
			run.invocations[key] = struct{}{}
			run.usageWithInvocationID++
			aggregate.rootInvocations[key] = struct{}{}
			aggregate.rootUsageWithInvocationID++
		}
	}
	return aggregate
}

func (service *Service) addContext(report *Report, evidence metrics.TokenAnalysisEvidence) {
	records := evidence.PromptRecords
	if len(records) == 0 {
		state := detailedState(report)
		report.Summary.CompiledMaterialized = MetricValue{State: state}
		report.Summary.ControllableMaterialized = MetricValue{State: state}
		report.Summary.ControllableExposure = MetricValue{State: state}
		report.Summary.RequiredPayload = MetricValue{State: state}
		report.Summary.ContextPartition = unavailableContextPartition(state)
		for index := range report.Runs {
			report.Runs[index].CompiledMaterialized = MetricValue{State: state}
			report.Runs[index].ControllableMaterialized = MetricValue{State: state}
			report.Runs[index].ControllableExposure = MetricValue{State: state}
			report.Runs[index].RequiredPayloadMaterialized = MetricValue{State: state}
		}
		if state != StateDisabled {
			report.DataQuality.Diagnostics = append(report.DataQuality.Diagnostics, "no_boundaries")
		}
		return
	}
	context := service.aggregateContext(evidence)
	exposureState := exposureState(context.launches, context.exposureByRun)
	for _, block := range context.blocks {
		if block.summary.InjectionCount > 0 {
			block.summary.MeanWhenInjected = float64(block.summary.MaterializedEstimate) / float64(block.summary.InjectionCount)
		}
		if len(context.launches) > 0 {
			block.summary.InjectionCoverage = float64(len(block.runs)) / float64(len(context.launches))
		}
		if exposureState != StateAvailable {
			block.summary.ExposureCount = 0
			block.summary.ExposureEstimate = nil
			block.summary.ControllableExposure = nil
		}
		if eligibleControl(blockMetadata{Origin: block.summary.Origin, Control: block.summary.Control}) &&
			block.summary.ExposureEstimate != nil &&
			block.target != nil {
			target := saturatingMultiply(*block.target, block.summary.ExposureCount)
			addressable := saturatingSubtract(*block.summary.ExposureEstimate, target)
			block.summary.AddressableEstimate = &addressable
		}
		report.Ledger = append(report.Ledger, block.summary)
	}
	sort.Slice(report.Ledger, func(i, j int) bool {
		if report.Ledger[i].MaterializedEstimate != report.Ledger[j].MaterializedEstimate {
			return report.Ledger[i].MaterializedEstimate > report.Ledger[j].MaterializedEstimate
		}
		return report.Ledger[i].BlockID < report.Ledger[j].BlockID
	})
	if len(report.Ledger) > maxReportLedger {
		report.Ledger = report.Ledger[:maxReportLedger]
		report.DataQuality.State = StateLimited
		report.DataQuality.Truncated = true
		report.DataQuality.Diagnostics = append(report.DataQuality.Diagnostics, "ledger_truncated")
	}
	report.Summary.CompiledMaterialized = availableValue(context.compiled)
	report.Summary.ControllableMaterialized = availableValue(context.controllable)
	report.Summary.RequiredPayload = availableValue(context.required)
	report.Summary.ContextPartition = ContextPartition{
		AgentTeamOwned:         availableValue(context.partition.agentteamOwned),
		WorkspaceConfiguration: availableValue(context.partition.workspaceConfiguration),
		UserTaskPayload:        availableValue(context.partition.userTaskPayload),
		Unknown:                availableValue(context.partition.unknown),
	}
	if exposureState == StateAvailable {
		var controllableExposure uint64
		for _, block := range context.blocks {
			if block.summary.ControllableExposure != nil {
				controllableExposure += *block.summary.ControllableExposure
			}
		}
		report.Summary.ControllableExposure = availableValue(controllableExposure)
	} else {
		report.Summary.ControllableExposure = MetricValue{State: exposureState}
		report.DataQuality.Diagnostics = append(report.DataQuality.Diagnostics, exposureDiagnostic(exposureState))
	}
	for index := range report.Runs {
		totals, ok := context.totalsByRun[report.Runs[index].RunRef]
		if !ok {
			report.Runs[index].CompiledMaterialized = MetricValue{State: StateNoData}
			report.Runs[index].ControllableMaterialized = MetricValue{State: StateNoData}
			report.Runs[index].ControllableExposure = MetricValue{State: StateNoData}
			report.Runs[index].RequiredPayloadMaterialized = MetricValue{State: StateNoData}
			continue
		}
		report.Runs[index].CompiledMaterialized = availableValue(totals.compiled)
		report.Runs[index].ControllableMaterialized = availableValue(totals.controllable)
		runCoverage := context.exposureByRun[report.Runs[index].RunRef]
		switch {
		case runCoverage.identityUnsupported && runCoverage.expectedInvocations == 0 &&
			runCoverage.observations == 0:
			report.Runs[index].ControllableExposure = MetricValue{State: StateUnsupported}
		case runCoverage.identityUnsupported,
			runCoverage.observations != runCoverage.expectedInvocations,
			runCoverage.complete != runCoverage.observations:
			report.Runs[index].ControllableExposure = MetricValue{State: StateLimited}
		case runCoverage.expectedInvocations == 0:
			report.Runs[index].ControllableExposure = MetricValue{State: StateNoData}
		default:
			report.Runs[index].ControllableExposure = availableValue(totals.exposure)
		}
		report.Runs[index].RequiredPayloadMaterialized = availableValue(totals.required)
	}

	for _, block := range report.Ledger {
		if !eligibleControl(blockMetadata{Origin: block.Origin, Control: block.Control}) {
			continue
		}
		estimate := block.MaterializedEstimate
		estimator := "materialized_contribution"
		if block.ControllableExposure != nil {
			estimate = *block.ControllableExposure
			estimator = "exposure_contribution"
		}
		if block.AddressableEstimate != nil {
			estimate = *block.AddressableEstimate
			estimator = "addressable_exposure_target"
		}
		if estimate == 0 {
			continue
		}
		report.Opportunities = append(report.Opportunities, Opportunity{
			BlockID:            block.BlockID,
			DisplayName:        block.DisplayName,
			Origin:             block.Origin,
			Control:            block.Control,
			Estimate:           estimate,
			Estimator:          estimator,
			SuggestedAction:    block.SuggestedAction,
			RequiredValidation: append([]string(nil), block.RequiredValidation...),
		})
	}
	sort.Slice(report.Opportunities, func(i, j int) bool {
		if report.Opportunities[i].Estimate != report.Opportunities[j].Estimate {
			return report.Opportunities[i].Estimate > report.Opportunities[j].Estimate
		}
		return report.Opportunities[i].BlockID < report.Opportunities[j].BlockID
	})
	if len(report.Opportunities) > 12 {
		report.Opportunities = report.Opportunities[:12]
	}
}

func (service *Service) aggregateContext(evidence metrics.TokenAnalysisEvidence) contextAggregation {
	context := contextAggregation{
		blocks:        make(map[string]*contextBlockAggregate),
		totalsByRun:   make(map[string]contextRunTotals),
		exposureByRun: make(map[string]exposureRunCoverage),
		launches:      make(map[string]struct{}),
	}
	for _, record := range evidence.PromptRecords {
		blockID := safeBlockID(record.BlockID)
		metadata := metadataFor(blockID, record.BlockLayer)
		block := context.blocks[blockID]
		if block == nil {
			block = &contextBlockAggregate{
				summary: BlockSummary{
					BlockID:            blockID,
					DisplayName:        metadata.DisplayName,
					Origin:             metadata.Origin,
					Control:            metadata.Control,
					Lifetime:           lifetimeFor(record.Durability),
					SuggestedAction:    metadata.SuggestedAction,
					RequiredValidation: append([]string(nil), metadata.Validation...),
				},
				runs:   make(map[string]struct{}),
				target: metadata.TargetPerInjection,
			}
			context.blocks[blockID] = block
		}
		count := record.Records
		if count == 0 {
			count = 1
		}
		block.summary.InjectionCount += count
		block.summary.MaterializedEstimate += record.Tokens
		block.runs[record.LaunchID] = struct{}{}
		context.launches[service.runRef(record.LaunchID)] = struct{}{}
		context.compiled += record.Tokens
		runRef := service.runRef(record.LaunchID)
		run := context.totalsByRun[runRef]
		run.compiled += record.Tokens
		if eligibleControl(metadata) {
			context.controllable += record.Tokens
			run.controllable += record.Tokens
		}
		switch metadata.Origin {
		case "agentteam_policy", "agentteam_generated":
			context.partition.agentteamOwned += record.Tokens
		case "workspace_configuration":
			context.partition.workspaceConfiguration += record.Tokens
		case "user_task_payload":
			context.partition.userTaskPayload += record.Tokens
		default:
			context.partition.unknown += record.Tokens
		}
		if metadata.Origin == "user_task_payload" || metadata.Origin == "workspace_configuration" {
			context.required += record.Tokens
			run.required += record.Tokens
		}
		context.totalsByRun[runRef] = run
	}
	for _, coverage := range evidence.ExposureCoverage {
		runRef := service.runRef(coverage.LaunchID)
		run := context.exposureByRun[runRef]
		run.observations = coverage.Observations
		run.complete = coverage.CompleteObservations
		context.exposureByRun[runRef] = run
	}
	invocationsByRun := make(map[string]map[string]struct{})
	for _, usage := range evidence.Usage {
		if usage.ExposureExcluded {
			continue
		}
		runRef := service.runRef(usage.LaunchID)
		run := context.exposureByRun[runRef]
		invocationID := strings.TrimSpace(usage.InvocationID)
		if invocationID == "" {
			run.identityUnsupported = true
			context.exposureByRun[runRef] = run
			continue
		}
		invocations := invocationsByRun[runRef]
		if invocations == nil {
			invocations = make(map[string]struct{})
			invocationsByRun[runRef] = invocations
		}
		key := usage.Runtime + "\x00" + usage.SessionID + "\x00" + invocationID
		invocations[key] = struct{}{}
	}
	for runRef, invocations := range invocationsByRun {
		run := context.exposureByRun[runRef]
		run.expectedInvocations = uint64(len(invocations))
		context.exposureByRun[runRef] = run
	}
	for _, exposure := range evidence.BlockExposures {
		blockID := safeBlockID(exposure.BlockID)
		block := context.blocks[blockID]
		if block == nil {
			continue
		}
		block.summary.ExposureCount += exposure.ExposureCount
		block.summary.ExposureEstimate = addOptional(block.summary.ExposureEstimate, exposure.Tokens)
		if eligibleControl(blockMetadata{Origin: block.summary.Origin, Control: block.summary.Control}) {
			block.summary.ControllableExposure = addOptional(block.summary.ControllableExposure, exposure.Tokens)
			runRef := service.runRef(exposure.LaunchID)
			run := context.totalsByRun[runRef]
			run.exposure += exposure.Tokens
			context.totalsByRun[runRef] = run
		}
	}
	return context
}

func exposureState(launches map[string]struct{}, coverage map[string]exposureRunCoverage) string {
	if len(launches) == 0 {
		return StateUnsupported
	}
	available := 0
	unsupported := 0
	for launchID := range launches {
		run := coverage[launchID]
		switch {
		case run.expectedInvocations == 0 && run.observations == 0 && !run.identityUnsupported:
			continue
		case run.identityUnsupported && run.expectedInvocations == 0 && run.observations == 0:
			unsupported++
		case run.identityUnsupported,
			run.observations != run.expectedInvocations,
			run.complete != run.observations:
			return StateLimited
		default:
			available++
		}
	}
	if available == 0 {
		return StateUnsupported
	}
	if unsupported > 0 {
		return StateLimited
	}
	return StateAvailable
}

func exposureDiagnostic(state string) string {
	if state == StateLimited {
		return "exposure_correlation_incomplete"
	}
	return "exposure_correlation_unsupported"
}

func addOptional(value *uint64, add uint64) *uint64 {
	if value == nil {
		value = new(uint64)
	}
	*value += add
	return value
}

func (service *Service) visibleAgents(human bool, agentID string) []Agent {
	all := service.deps.Agents()
	if human {
		return all
	}
	agentID = strings.TrimSpace(agentID)
	for _, configured := range all {
		if configured.ID == agentID {
			return []Agent{configured}
		}
	}
	return []Agent{{ID: agentID}}
}

func (service *Service) resolveAgentRef(ref string, agents []Agent) string {
	ref = strings.TrimSpace(ref)
	if ref == "" {
		return ""
	}
	for _, configured := range agents {
		if ref == service.agentRef(configured.ID) {
			return configured.ID
		}
	}
	return ""
}

func (service *Service) resolveRunRef(ref string, agentIDs []string, window metrics.TimeWindow, snapshotAt time.Time) (string, string) {
	ref = strings.TrimSpace(ref)
	if ref == "" || service.deps.Evidence == nil {
		return "", ""
	}
	evidence := service.deps.Evidence.QueryTokenAnalysisEvidence(metrics.TokenEvidenceQuery{
		Window:     window,
		AgentIDs:   agentIDs,
		SnapshotAt: snapshotAt.UnixMilli(),
	})
	for _, record := range evidence.Usage {
		if ref == service.runRef(record.LaunchID) {
			return record.LaunchID, record.AgentID
		}
	}
	for _, record := range evidence.PromptRecords {
		if ref == service.runRef(record.LaunchID) {
			return record.LaunchID, record.AgentID
		}
	}
	return "", ""
}

func (service *Service) agentRef(agentID string) string { return service.opaqueRef("agent", agentID) }
func (service *Service) runRef(runID string) string     { return service.opaqueRef("run", runID) }

func (service *Service) opaqueRef(kind, raw string) string {
	sum := sha256.Sum256([]byte(
		hex.EncodeToString(service.refKey[:]) + "\x00" + kind + "\x00" + strings.TrimSpace(raw),
	))
	return kind + "_" + hex.EncodeToString(sum[:8])
}

func ParseWindow(value string) (metrics.TimeWindow, error) {
	switch strings.TrimSpace(value) {
	case "", "1d", "24h":
		return metrics.TimeWindow1Day, nil
	case "6h":
		return metrics.TimeWindow6Hours, nil
	case "12h":
		return metrics.TimeWindow12Hours, nil
	case "3d":
		return metrics.TimeWindow3Days, nil
	case "1w":
		return metrics.TimeWindow1Week, nil
	default:
		return 0, fmt.Errorf("invalid window %q (valid: 6h, 12h, 1d/24h, 3d, 1w)", value)
	}
}

func (service *Service) activeLaunches() []string {
	if service.deps.ActiveLaunches == nil {
		return nil
	}
	return service.deps.ActiveLaunches()
}

func launchStartedAt(evidence metrics.TokenAnalysisEvidence) *time.Time {
	var earliest int64
	for _, record := range evidence.Usage {
		if record.LaunchStartedAt > 0 && (earliest == 0 || record.LaunchStartedAt < earliest) {
			earliest = record.LaunchStartedAt
		}
	}
	for _, record := range evidence.PromptRecords {
		if record.LaunchStartedAt > 0 && (earliest == 0 || record.LaunchStartedAt < earliest) {
			earliest = record.LaunchStartedAt
		}
	}
	if earliest == 0 {
		return nil
	}
	startedAt := time.UnixMilli(earliest).UTC()
	return &startedAt
}

func normalizedExperimentValue(value, fallback string) string {
	value = strings.TrimSpace(value)
	if value == "" {
		return fallback
	}
	return value
}

var changedVariablePattern = regexp.MustCompile(`^[a-z0-9][a-z0-9._-]{0,127}$`)

func validateExperiment(request Request) error {
	experiment := request.Experiment
	if !validComparabilityValue(
		normalizedExperimentValue(experiment.CacheState, "unknown"),
		"cold",
		"warm",
		"unknown",
	) {
		return fmt.Errorf("invalid cache state %q (valid: cold, warm, unknown)", experiment.CacheState)
	}
	if !validComparabilityValue(
		normalizedExperimentValue(experiment.Outcome, "unknown"),
		"success",
		"failure",
		"unknown",
	) {
		return fmt.Errorf("invalid outcome %q (valid: success, failure, unknown)", experiment.Outcome)
	}
	changedVariable := strings.TrimSpace(experiment.ChangedVariable)
	if changedVariable != "" {
		if strings.TrimSpace(request.RunRef) == "" {
			return fmt.Errorf("changed_variable_requires_run")
		}
		if !changedVariablePattern.MatchString(changedVariable) {
			return fmt.Errorf("invalid_changed_variable")
		}
	}
	for _, value := range []string{experiment.Case, experiment.Workspace, experiment.Tools} {
		if len(strings.TrimSpace(value)) > 512 {
			return fmt.Errorf("experiment_metadata_too_long")
		}
	}
	return nil
}

func declaredFingerprint(kind, value string) string {
	value = strings.TrimSpace(value)
	if value == "" {
		return ""
	}
	sum := sha256.Sum256([]byte(kind + "\x00" + value))
	return hex.EncodeToString(sum[:8])
}

func unavailableContextPartition(state string) ContextPartition {
	return ContextPartition{
		AgentTeamOwned:         MetricValue{State: state},
		WorkspaceConfiguration: MetricValue{State: state},
		UserTaskPayload:        MetricValue{State: state},
		Unknown:                MetricValue{State: state},
	}
}

func availableValue(value uint64) MetricValue {
	copy := value
	return MetricValue{State: StateAvailable, Value: &copy}
}

func detailedState(report *Report) string {
	if report != nil && !report.Collection.Enabled {
		return StateDisabled
	}
	return StateNoData
}

func eligibleControl(metadata blockMetadata) bool {
	if metadata.Control == "direct" {
		return metadata.Origin == "agentteam_policy" || metadata.Origin == "agentteam_generated"
	}
	return metadata.Control == "assembly" && metadata.Origin != "user_task_payload"
}

func saturatingSubtract(total, part uint64) uint64 {
	if part >= total {
		return 0
	}
	return total - part
}

func saturatingMultiply(left, right uint64) uint64 {
	if left != 0 && right > ^uint64(0)/left {
		return ^uint64(0)
	}
	return left * right
}

func scopeFingerprint(window, kind string, agentCount int) string {
	sum := sha256.Sum256([]byte(fmt.Sprintf("%s\x00%s\x00%d", window, kind, agentCount)))
	return "scope_" + hex.EncodeToString(sum[:8])
}

func taskPayloadFP(records []metrics.PromptCompileRecord, runID string) string {
	runID = strings.TrimSpace(runID)
	if runID == "" {
		return ""
	}
	values := make(map[string]struct{})
	for _, record := range records {
		if strings.TrimSpace(record.LaunchID) != runID {
			continue
		}
		value := strings.TrimSpace(record.TaskPayloadFP)
		if !record.TaskPayloadPresent {
			if value != "" {
				return ""
			}
			continue
		}
		if value == "" {
			return ""
		}
		if !lowerHexFingerprint(value) {
			return ""
		}
		values[value] = struct{}{}
		if len(values) > 1 {
			return ""
		}
	}
	if len(values) != 1 {
		return ""
	}
	for value := range values {
		return value
	}
	return ""
}

func reportFingerprint(report Report) string {
	report.Fingerprint = ""
	report.GeneratedAt = time.Time{}
	report.SnapshotAt = time.Time{}
	report.Collection.AutoEnabled = false
	report.Collection.Stopped = false
	data, err := json.Marshal(report)
	if err != nil {
		return ""
	}
	sum := sha256.Sum256(data)
	return hex.EncodeToString(sum[:8])
}

func firstNonEmpty(values ...string) string {
	for _, value := range values {
		if value = strings.TrimSpace(value); value != "" {
			return value
		}
	}
	return ""
}
