package service

import (
	"errors"
	"fmt"
	"strings"
	"testing"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"go.uber.org/zap"
	"go.uber.org/zap/zaptest/observer"
)

func TestSingleRequestMetrics(t *testing.T) {
	if first, second := defaultSingleRequestCollectorSet(), defaultSingleRequestCollectorSet(); first != second {
		t.Fatal("default collector set was registered more than once")
	}
	registry := prometheus.NewRegistry()
	core, logs := observer.New(zap.InfoLevel)
	collector := newSingleRequestObservability(registry, zap.New(core))
	clock := newSingleRequestManualClock(time.Unix(0, 0))
	accumulator := newSingleRequestTimingAccumulator(clock, collector)

	accumulator.onRequest()
	accumulator.onStageEnter(singleRequestStagePlan)
	clock.Advance(2 * time.Second)
	accumulator.onStageExit(singleRequestStagePlan, singleRequestOutcomeSuccess, "")
	accumulator.onTerminal(singleRequestOutcomeSuccess, "", true)
	accumulator.onTerminal(singleRequestOutcomeError, singleRequestErrorClassProvider, false)

	terminalLabels := map[string]string{
		"event_class": "terminal", "stage": "none", "operation": "terminal", "outcome": "success", "error_class": "none",
	}
	if got := metricValue(t, registry, singleRequestLifecycleMetric, terminalLabels); got != 1 {
		t.Fatalf("terminal metric = %v, want 1", got)
	}
	stageLabels := map[string]string{
		"event_class": "stage", "stage": "plan", "operation": "plan", "outcome": "success", "error_class": "none",
	}
	if got := metricValue(t, registry, singleRequestLifecycleMetric, stageLabels); got != 1 {
		t.Fatalf("stage metric = %v, want 1", got)
	}

	families, err := registry.Gather()
	if err != nil {
		t.Fatalf("gather metrics: %v", err)
	}
	for _, family := range families {
		for _, metric := range family.Metric {
			for _, label := range metric.Label {
				if label.GetName() == "correlation" || strings.Contains(label.GetValue(), "sr-") {
					t.Fatalf("unbounded correlation label: %s=%q", label.GetName(), label.GetValue())
				}
			}
		}
	}

	allowed := map[string]bool{
		"correlation": true, "event_class": true, "stage": true, "operation": true,
		"outcome": true, "error_class": true, "duration_ms": true, "tool_count": true, "has_result": true,
	}
	entries := logs.All()
	if len(entries) != 3 {
		t.Fatalf("observation logs = %d, want 3", len(entries))
	}
	for _, entry := range entries {
		if entry.Message != singleRequestObservationLogKey {
			t.Fatalf("log message = %q", entry.Message)
		}
		if len(entry.Context) != len(allowed) {
			t.Fatalf("log field count = %d, want %d", len(entry.Context), len(allowed))
		}
		for _, field := range entry.Context {
			if !allowed[field.Key] {
				t.Fatalf("unexpected log key %q", field.Key)
			}
		}
	}
}

func TestSingleRequestMetricsRejectSecretSentinelAndIsolatesObserverFailures(t *testing.T) {
	registry := prometheus.NewRegistry()
	core, logs := observer.New(zap.InfoLevel)
	collector := newSingleRequestObservability(registry, zap.New(core))
	if err := collector.Emit(singleRequestDTO{
		EventClass: singleRequestEventClassTerminal, Operation: singleRequestOperationTerminal,
		Outcome: singleRequestOutcomeError, ErrorClass: singleRequestErrorClassProvider,
		Correlation: "SECRET_PATH_COMMAND_BEARER", DurationMS: 1,
	}); err != nil {
		t.Fatalf("Emit: %v", err)
	}
	for _, entry := range logs.All() {
		if strings.Contains(strings.ToLower(fmt.Sprint(entry.ContextMap()["correlation"])), "secret") {
			t.Fatalf("secret sentinel leaked: %+v", entry)
		}
	}

	failing := singleRequestObserverFunc(func(singleRequestDTO) error { return errors.New("observer failure") })
	accumulator := newSingleRequestTimingAccumulator(nil, failing)
	accumulator.onTerminal(singleRequestOutcomeSuccess, "", true)
	if got := accumulator.observer.failureCount(); got != 1 {
		t.Fatalf("isolated failures = %d, want 1", got)
	}
}

type singleRequestObserverFunc func(singleRequestDTO) error

func (fn singleRequestObserverFunc) Emit(dto singleRequestDTO) error { return fn(dto) }
