package sandbox

import (
	"strings"
	"testing"
)

func TestLimitedBuffer_UnderLimit(t *testing.T) {
	lb := &limitedBuffer{max: 100}
	n, err := lb.Write([]byte("hello"))
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if n != 5 {
		t.Errorf("expected 5, got %d", n)
	}
	if lb.String() != "hello" {
		t.Errorf("expected 'hello', got %q", lb.String())
	}
	if lb.truncated {
		t.Error("should not be truncated")
	}
}

func TestLimitedBuffer_AtLimit(t *testing.T) {
	lb := &limitedBuffer{max: 5}
	lb.Write([]byte("hello"))
	if lb.truncated {
		t.Error("exactly at limit should not be truncated")
	}
	if lb.String() != "hello" {
		t.Errorf("expected 'hello', got %q", lb.String())
	}
}

func TestLimitedBuffer_OverLimit(t *testing.T) {
	lb := &limitedBuffer{max: 5}
	n, err := lb.Write([]byte("hello world"))
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	// Should report all bytes as "written" (consumed) even though truncated
	if n != 11 {
		t.Errorf("expected 11 (full input consumed), got %d", n)
	}
	if lb.String() != "hello" {
		t.Errorf("expected 'hello', got %q", lb.String())
	}
	if !lb.truncated {
		t.Error("should be truncated")
	}
}

func TestLimitedBuffer_MultipleWrites(t *testing.T) {
	lb := &limitedBuffer{max: 10}
	lb.Write([]byte("aaaa"))
	lb.Write([]byte("bbbb"))
	lb.Write([]byte("cccc")) // should be partially truncated

	if lb.buf.Len() != 10 {
		t.Errorf("expected 10 bytes, got %d", lb.buf.Len())
	}
	if !lb.truncated {
		t.Error("should be truncated after exceeding max")
	}
	if lb.String() != "aaaabbbbcc" {
		t.Errorf("expected 'aaaabbbbcc', got %q", lb.String())
	}
}

func TestLimitedBuffer_DiscardAfterTruncation(t *testing.T) {
	lb := &limitedBuffer{max: 3}
	lb.Write([]byte("abc"))
	lb.Write([]byte("def")) // should be silently discarded

	if lb.String() != "abc" {
		t.Errorf("expected 'abc', got %q", lb.String())
	}
	if !lb.truncated {
		t.Error("should be truncated")
	}
}

func TestDefaultConfig_MaxOutputBytes(t *testing.T) {
	cfg := DefaultConfig()
	if cfg.MaxOutputBytes != 1<<20 {
		t.Errorf("expected 1MB default, got %d", cfg.MaxOutputBytes)
	}
}

func TestSanitizeKey(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		{"agent:main:telegram:direct:123", "agent-main-telegram-direct-123"},
		{"simple", "simple"},
		{"has/slash", "has-slash"},
		{"has space", "has-space"},
		{strings.Repeat("x", 100), strings.Repeat("x", 50)},
	}
	for _, tc := range tests {
		got := sanitizeKey(tc.input)
		if got != tc.expected {
			t.Errorf("sanitizeKey(%q) = %q, want %q", tc.input, got, tc.expected)
		}
	}
}

func TestResolveScopeKey(t *testing.T) {
	tests := []struct {
		scope    Scope
		key      string
		expected string
	}{
		{ScopeShared, "agent:main:telegram:direct:123", "shared"},
		{ScopeAgent, "agent:main:telegram:direct:123", "agent:main"},
		{ScopeSession, "agent:main:telegram:direct:123", "agent:main:telegram:direct:123"},
		{ScopeSession, "", "default"},
	}
	for _, tc := range tests {
		cfg := Config{Scope: tc.scope}
		got := cfg.ResolveScopeKey(tc.key)
		if got != tc.expected {
			t.Errorf("scope=%s key=%q → %q, want %q", tc.scope, tc.key, got, tc.expected)
		}
	}
}
