package claudecode

import (
	"testing"
	"time"

	"github.com/orchard9/rdev/internal/domain"
)

func TestParseStreamMessage_Init(t *testing.T) {
	line := []byte(`{"type":"init","session_id":"abc123","timestamp":"2024-01-01T00:00:00Z"}`)

	msg, err := ParseStreamMessage(line)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	if msg.Type != StreamMessageInit {
		t.Errorf("expected type 'init', got %q", msg.Type)
	}
	if msg.SessionID != "abc123" {
		t.Errorf("expected session_id 'abc123', got %q", msg.SessionID)
	}
}

func TestParseStreamMessage_Message(t *testing.T) {
	line := []byte(`{"type":"message","role":"assistant","content":[{"type":"text","text":"Hello, world!"}]}`)

	msg, err := ParseStreamMessage(line)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	if msg.Type != StreamMessageMessage {
		t.Errorf("expected type 'message', got %q", msg.Type)
	}
	if msg.Role != "assistant" {
		t.Errorf("expected role 'assistant', got %q", msg.Role)
	}
	if len(msg.Content) != 1 {
		t.Fatalf("expected 1 content block, got %d", len(msg.Content))
	}
	if msg.Content[0].Text != "Hello, world!" {
		t.Errorf("expected text 'Hello, world!', got %q", msg.Content[0].Text)
	}
}

func TestParseStreamMessage_ToolUse(t *testing.T) {
	line := []byte(`{"type":"tool_use","name":"Bash","input":{"command":"ls -la"}}`)

	msg, err := ParseStreamMessage(line)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	if msg.Type != StreamMessageToolUse {
		t.Errorf("expected type 'tool_use', got %q", msg.Type)
	}
	if msg.Name != "Bash" {
		t.Errorf("expected name 'Bash', got %q", msg.Name)
	}
}

func TestParseStreamMessage_ToolResult(t *testing.T) {
	line := []byte(`{"type":"tool_result","output":"total 64\ndrwxr-xr-x  10 user  staff"}`)

	msg, err := ParseStreamMessage(line)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	if msg.Type != StreamMessageToolResult {
		t.Errorf("expected type 'tool_result', got %q", msg.Type)
	}
	if msg.Output != "total 64\ndrwxr-xr-x  10 user  staff" {
		t.Errorf("unexpected output: %q", msg.Output)
	}
}

func TestParseStreamMessage_Result(t *testing.T) {
	tests := []struct {
		name        string
		line        string
		wantSubtype string
		wantIsError bool
		wantMs      int64
	}{
		{
			name:        "success",
			line:        `{"type":"result","subtype":"success","is_error":false,"duration_ms":1234}`,
			wantSubtype: "success",
			wantIsError: false,
			wantMs:      1234,
		},
		{
			name:        "error",
			line:        `{"type":"result","subtype":"error","is_error":true,"error":"something went wrong"}`,
			wantSubtype: "error",
			wantIsError: true,
			wantMs:      0,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			msg, err := ParseStreamMessage([]byte(tt.line))
			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}

			if msg.Type != StreamMessageResult {
				t.Errorf("expected type 'result', got %q", msg.Type)
			}
			if msg.Subtype != tt.wantSubtype {
				t.Errorf("expected subtype %q, got %q", tt.wantSubtype, msg.Subtype)
			}
			if msg.IsError != tt.wantIsError {
				t.Errorf("expected is_error %v, got %v", tt.wantIsError, msg.IsError)
			}
			if msg.DurationMs != tt.wantMs {
				t.Errorf("expected duration_ms %d, got %d", tt.wantMs, msg.DurationMs)
			}
		})
	}
}

func TestParseStreamMessage_Invalid(t *testing.T) {
	line := []byte(`not valid json`)

	_, err := ParseStreamMessage(line)
	if err == nil {
		t.Error("expected error for invalid JSON")
	}
}

func TestStreamMessage_ToAgentEvent_Init(t *testing.T) {
	msg := &StreamMessage{
		Type:      StreamMessageInit,
		SessionID: "test-session",
		Timestamp: "2024-01-01T12:00:00Z",
	}

	event := msg.ToAgentEvent()

	if event.Type != domain.AgentEventOutput {
		t.Errorf("expected type %v, got %v", domain.AgentEventOutput, event.Type)
	}
	if event.Content != "Session started" {
		t.Errorf("expected content 'Session started', got %q", event.Content)
	}
	if event.Metadata["session_id"] != "test-session" {
		t.Errorf("expected session_id in metadata")
	}
}

func TestStreamMessage_ToAgentEvent_Message(t *testing.T) {
	msg := &StreamMessage{
		Type: StreamMessageMessage,
		Role: "assistant",
		Content: []ContentBlock{
			{Type: "text", Text: "Hello from Claude"},
		},
	}

	event := msg.ToAgentEvent()

	if event.Type != domain.AgentEventOutput {
		t.Errorf("expected type %v, got %v", domain.AgentEventOutput, event.Type)
	}
	if event.Content != "Hello from Claude" {
		t.Errorf("expected content 'Hello from Claude', got %q", event.Content)
	}
}

func TestStreamMessage_ToAgentEvent_ToolUse(t *testing.T) {
	msg := &StreamMessage{
		Type:  StreamMessageToolUse,
		Name:  "Read",
		Input: []byte(`{"path":"/workspace/main.go"}`),
	}

	event := msg.ToAgentEvent()

	if event.Type != domain.AgentEventToolUse {
		t.Errorf("expected type %v, got %v", domain.AgentEventToolUse, event.Type)
	}
	if event.ToolName != "Read" {
		t.Errorf("expected tool name 'Read', got %q", event.ToolName)
	}
	if event.ToolInput["path"] != "/workspace/main.go" {
		t.Errorf("expected path in tool input")
	}
}

func TestStreamMessage_ToAgentEvent_ToolResult(t *testing.T) {
	msg := &StreamMessage{
		Type:   StreamMessageToolResult,
		Output: "file contents here",
	}

	event := msg.ToAgentEvent()

	if event.Type != domain.AgentEventToolResult {
		t.Errorf("expected type %v, got %v", domain.AgentEventToolResult, event.Type)
	}
	if event.Content != "file contents here" {
		t.Errorf("expected content 'file contents here', got %q", event.Content)
	}
}

func TestStreamMessage_ToAgentEvent_ResultSuccess(t *testing.T) {
	msg := &StreamMessage{
		Type:       StreamMessageResult,
		Subtype:    "success",
		IsError:    false,
		DurationMs: 5000,
	}

	event := msg.ToAgentEvent()

	if event.Type != domain.AgentEventComplete {
		t.Errorf("expected type %v, got %v", domain.AgentEventComplete, event.Type)
	}
	if event.Metadata["status"] != "success" {
		t.Errorf("expected status in metadata")
	}
	if event.Metadata["duration_ms"].(int64) != 5000 {
		t.Errorf("expected duration_ms in metadata")
	}
}

func TestStreamMessage_ToAgentEvent_ResultError(t *testing.T) {
	msg := &StreamMessage{
		Type:    StreamMessageResult,
		Subtype: "error",
		IsError: true,
		Error:   "execution failed",
	}

	event := msg.ToAgentEvent()

	if event.Type != domain.AgentEventError {
		t.Errorf("expected type %v, got %v", domain.AgentEventError, event.Type)
	}
	if event.Content != "execution failed" {
		t.Errorf("expected error content, got %q", event.Content)
	}
}

func TestStreamMessage_IsTerminal(t *testing.T) {
	tests := []struct {
		msgType  StreamMessageType
		terminal bool
	}{
		{StreamMessageInit, false},
		{StreamMessageMessage, false},
		{StreamMessageToolUse, false},
		{StreamMessageToolResult, false},
		{StreamMessageResult, true},
	}

	for _, tt := range tests {
		t.Run(string(tt.msgType), func(t *testing.T) {
			msg := &StreamMessage{Type: tt.msgType}
			if msg.IsTerminal() != tt.terminal {
				t.Errorf("IsTerminal() = %v, want %v", msg.IsTerminal(), tt.terminal)
			}
		})
	}
}

func TestStreamMessage_IsSuccess(t *testing.T) {
	tests := []struct {
		name    string
		msg     StreamMessage
		success bool
	}{
		{"success result", StreamMessage{Type: StreamMessageResult, Subtype: "success", IsError: false}, true},
		{"error result", StreamMessage{Type: StreamMessageResult, Subtype: "error", IsError: true}, false},
		{"non-result", StreamMessage{Type: StreamMessageMessage}, false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if tt.msg.IsSuccess() != tt.success {
				t.Errorf("IsSuccess() = %v, want %v", tt.msg.IsSuccess(), tt.success)
			}
		})
	}
}

func TestParseTimestamp(t *testing.T) {
	tests := []struct {
		input string
		valid bool
	}{
		{"2024-01-01T12:00:00Z", true},
		{"2024-01-01T12:00:00+00:00", true},
		{"invalid", false},
		{"", false},
	}

	for _, tt := range tests {
		t.Run(tt.input, func(t *testing.T) {
			result := parseTimestamp(tt.input)
			if tt.valid {
				if result.Year() != 2024 {
					t.Errorf("expected year 2024, got %d", result.Year())
				}
			} else {
				// Should return current time for invalid input
				if time.Since(result) > time.Second {
					t.Error("expected recent time for invalid input")
				}
			}
		})
	}
}

func TestExtractTextContent(t *testing.T) {
	tests := []struct {
		name   string
		blocks []ContentBlock
		want   string
	}{
		{
			name:   "single text block",
			blocks: []ContentBlock{{Type: "text", Text: "hello"}},
			want:   "hello",
		},
		{
			name: "text after tool_use",
			blocks: []ContentBlock{
				{Type: "tool_use", Name: "Bash"},
				{Type: "text", Text: "result"},
			},
			want: "result",
		},
		{
			name:   "empty blocks",
			blocks: []ContentBlock{},
			want:   "",
		},
		{
			name:   "no text blocks",
			blocks: []ContentBlock{{Type: "tool_use", Name: "Read"}},
			want:   "",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := extractTextContent(tt.blocks); got != tt.want {
				t.Errorf("extractTextContent() = %q, want %q", got, tt.want)
			}
		})
	}
}
