package agent

import "testing"

func TestToolLoopDetection_NoLoop(t *testing.T) {
	var s toolLoopState

	// 2 identical calls with same result → below threshold, no detection
	for i := range 2 {
		h := s.record("list_files", map[string]any{"path": "."})
		s.recordResult(h, "access denied")
		level, _ := s.detect("list_files", h)
		if level != "" {
			t.Fatalf("iteration %d: expected no detection, got %q", i, level)
		}
	}
}

func TestToolLoopDetection_Warning(t *testing.T) {
	var s toolLoopState

	var lastLevel string
	for range toolLoopWarningThreshold {
		h := s.record("list_files", map[string]any{"path": "."})
		s.recordResult(h, "access denied")
		lastLevel, _ = s.detect("list_files", h)
	}
	if lastLevel != "warning" {
		t.Fatalf("expected warning after %d calls, got %q", toolLoopWarningThreshold, lastLevel)
	}
}

func TestToolLoopDetection_Critical(t *testing.T) {
	var s toolLoopState

	var lastLevel string
	for range toolLoopCriticalThreshold {
		h := s.record("list_files", map[string]any{"path": "."})
		s.recordResult(h, "access denied")
		lastLevel, _ = s.detect("list_files", h)
	}
	if lastLevel != "critical" {
		t.Fatalf("expected critical after %d calls, got %q", toolLoopCriticalThreshold, lastLevel)
	}
}

func TestToolLoopDetection_DifferentArgs(t *testing.T) {
	var s toolLoopState

	// Same tool but different args each time → no detection
	for i := range 15 {
		args := map[string]any{"path": string(rune('a' + i))}
		h := s.record("list_files", args)
		s.recordResult(h, "access denied")
		level, _ := s.detect("list_files", h)
		if level != "" {
			t.Fatalf("iteration %d: expected no detection for different args, got %q", i, level)
		}
	}
}

func TestToolLoopDetection_DifferentResults(t *testing.T) {
	var s toolLoopState

	// Same args but different results each time → progress, no detection
	for i := range 15 {
		h := s.record("web_fetch", map[string]any{"url": "https://example.com"})
		s.recordResult(h, "result content "+string(rune('a'+i)))
		level, _ := s.detect("web_fetch", h)
		if level != "" {
			t.Fatalf("iteration %d: expected no detection for different results, got %q", i, level)
		}
	}
}

func TestToolLoopDetection_MixedTools(t *testing.T) {
	var s toolLoopState

	// Alternate between two tools with same result → each tool only hit ~half
	// With 8 iterations, each tool is called 4 times → below critical (5)
	for i := range 8 {
		toolName := "list_files"
		if i%2 == 1 {
			toolName = "read_file"
		}
		h := s.record(toolName, map[string]any{"path": "."})
		s.recordResult(h, "error")
		level, _ := s.detect(toolName, h)
		// Each tool is only called 4 times, should at most warn
		if level == "critical" {
			t.Fatalf("iteration %d: unexpected critical for alternating tools", i)
		}
	}
}

func TestStableJSON(t *testing.T) {
	// Same keys in different order → same hash
	a := stableJSON(map[string]any{"b": 2, "a": 1})
	b := stableJSON(map[string]any{"a": 1, "b": 2})
	if a != b {
		t.Fatalf("stableJSON not deterministic: %q != %q", a, b)
	}
}

func TestHashToolCall(t *testing.T) {
	// Same input → same hash
	h1 := hashToolCall("list_files", map[string]any{"path": "."})
	h2 := hashToolCall("list_files", map[string]any{"path": "."})
	if h1 != h2 {
		t.Fatal("hashToolCall not deterministic")
	}

	// Different tool → different hash
	h3 := hashToolCall("read_file", map[string]any{"path": "."})
	if h1 == h3 {
		t.Fatal("different tools should have different hashes")
	}
}
