package townlog

import (
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"
)

func TestFormatLogLine(t *testing.T) {
	ts := time.Date(2025, 12, 26, 15, 30, 45, 0, time.UTC)

	tests := []struct {
		name     string
		event    Event
		contains []string
	}{
		{
			name: "spawn event",
			event: Event{
				Timestamp: ts,
				Type:      EventSpawn,
				Agent:     "gastown/crew/max",
				Context:   "gt-xyz",
			},
			contains: []string{"2025-12-26 15:30:45", "[spawn]", "gastown/crew/max", "spawned for gt-xyz"},
		},
		{
			name: "nudge event",
			event: Event{
				Timestamp: ts,
				Type:      EventNudge,
				Agent:     "gastown/crew/max",
				Context:   "start work",
			},
			contains: []string{"[nudge]", "gastown/crew/max", "nudged with"},
		},
		{
			name: "done event",
			event: Event{
				Timestamp: ts,
				Type:      EventDone,
				Agent:     "gastown/crew/max",
				Context:   "gt-abc",
			},
			contains: []string{"[done]", "completed gt-abc"},
		},
		{
			name: "crash event",
			event: Event{
				Timestamp: ts,
				Type:      EventCrash,
				Agent:     "gastown/polecats/Toast",
				Context:   "signal 9",
			},
			contains: []string{"[crash]", "exited unexpectedly", "signal 9"},
		},
		{
			name: "kill event",
			event: Event{
				Timestamp: ts,
				Type:      EventKill,
				Agent:     "gastown/polecats/Toast",
				Context:   "gt stop",
			},
			contains: []string{"[kill]", "killed", "gt stop"},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			line := formatLogLine(tt.event)
			for _, want := range tt.contains {
				if !strings.Contains(line, want) {
					t.Errorf("formatLogLine() = %q, want it to contain %q", line, want)
				}
			}
		})
	}
}

func TestParseLogLine(t *testing.T) {
	tests := []struct {
		name    string
		line    string
		wantErr bool
		check   func(Event) bool
	}{
		{
			name: "valid spawn line",
			line: "2025-12-26 15:30:45 [spawn] gastown/crew/max spawned for gt-xyz",
			check: func(e Event) bool {
				return e.Type == EventSpawn && e.Agent == "gastown/crew/max"
			},
		},
		{
			name: "valid nudge line",
			line: "2025-12-26 15:31:02 [nudge] gastown/crew/max nudged with \"start\"",
			check: func(e Event) bool {
				return e.Type == EventNudge && e.Agent == "gastown/crew/max"
			},
		},
		{
			name:    "too short",
			line:    "short",
			wantErr: true,
		},
		{
			name:    "missing bracket",
			line:    "2025-12-26 15:30:45 spawn gastown/crew/max",
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			event, err := parseLogLine(tt.line)
			if tt.wantErr {
				if err == nil {
					t.Errorf("parseLogLine() expected error, got nil")
				}
				return
			}
			if err != nil {
				t.Errorf("parseLogLine() unexpected error: %v", err)
				return
			}
			if tt.check != nil && !tt.check(event) {
				t.Errorf("parseLogLine() check failed for event: %+v", event)
			}
		})
	}
}

func TestLoggerLogEvent(t *testing.T) {
	// Create temp directory
	tmpDir, err := os.MkdirTemp("", "townlog-test")
	if err != nil {
		t.Fatalf("creating temp dir: %v", err)
	}
	defer os.RemoveAll(tmpDir)

	logger := NewLogger(tmpDir)

	// Log an event
	err = logger.Log(EventSpawn, "gastown/crew/max", "gt-xyz")
	if err != nil {
		t.Fatalf("Log() error: %v", err)
	}

	// Verify log file was created
	logPath := filepath.Join(tmpDir, "logs", "town.log")
	content, err := os.ReadFile(logPath)
	if err != nil {
		t.Fatalf("reading log file: %v", err)
	}

	if !strings.Contains(string(content), "[spawn]") {
		t.Errorf("log file should contain [spawn], got: %s", content)
	}
	if !strings.Contains(string(content), "gastown/crew/max") {
		t.Errorf("log file should contain agent name, got: %s", content)
	}
}

func TestFilterEvents(t *testing.T) {
	now := time.Now()
	events := []Event{
		{Timestamp: now.Add(-2 * time.Hour), Type: EventSpawn, Agent: "gastown/crew/max", Context: "gt-1"},
		{Timestamp: now.Add(-1 * time.Hour), Type: EventNudge, Agent: "gastown/crew/max", Context: "hi"},
		{Timestamp: now.Add(-30 * time.Minute), Type: EventDone, Agent: "gastown/polecats/Toast", Context: "gt-2"},
		{Timestamp: now.Add(-10 * time.Minute), Type: EventSpawn, Agent: "wyvern/crew/joe", Context: "gt-3"},
	}

	tests := []struct {
		name      string
		filter    Filter
		wantCount int
	}{
		{
			name:      "no filter",
			filter:    Filter{},
			wantCount: 4,
		},
		{
			name:      "filter by type",
			filter:    Filter{Type: EventSpawn},
			wantCount: 2,
		},
		{
			name:      "filter by agent prefix",
			filter:    Filter{Agent: "gastown/"},
			wantCount: 3,
		},
		{
			name:      "filter by time",
			filter:    Filter{Since: now.Add(-45 * time.Minute)},
			wantCount: 2,
		},
		{
			name:      "combined filters",
			filter:    Filter{Type: EventSpawn, Agent: "gastown/"},
			wantCount: 1,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := FilterEvents(events, tt.filter)
			if len(result) != tt.wantCount {
				t.Errorf("FilterEvents() got %d events, want %d", len(result), tt.wantCount)
			}
		})
	}
}

func TestTruncate(t *testing.T) {
	tests := []struct {
		input  string
		maxLen int
		want   string
	}{
		{"short", 10, "short"},
		{"exactly10c", 10, "exactly10c"},
		{"this is a longer string", 10, "this is..."},
	}

	for _, tt := range tests {
		t.Run(tt.input, func(t *testing.T) {
			got := truncate(tt.input, tt.maxLen)
			if got != tt.want {
				t.Errorf("truncate(%q, %d) = %q, want %q", tt.input, tt.maxLen, got, tt.want)
			}
		})
	}
}
