package converter

import (
	"fmt"
	"strings"
	"testing"
)

func TestConvertLineAssistant(t *testing.T) {
	line := `{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"Hello, world!"}]}}`
	result := ConvertLine(line, "chat-123")

	if result.Skip {
		t.Error("expected not Skip")
	}
	if result.Done {
		t.Error("expected not Done")
	}
	if result.Error != nil {
		t.Fatalf("unexpected error: %v", result.Error)
	}
	if result.Chunk == nil {
		t.Fatal("expected Chunk, got nil")
	}
	if result.Chunk.ID != "chat-123" {
		t.Errorf("Chunk.ID = %q, want %q", result.Chunk.ID, "chat-123")
	}
	if result.Chunk.Object != "chat.completion.chunk" {
		t.Errorf("Chunk.Object = %q, want %q", result.Chunk.Object, "chat.completion.chunk")
	}
	if len(result.Chunk.Choices) != 1 {
		t.Fatalf("len(Choices) = %d, want 1", len(result.Chunk.Choices))
	}
	if *result.Chunk.Choices[0].Delta.Content != "Hello, world!" {
		t.Errorf("Delta.Content = %q, want %q", *result.Chunk.Choices[0].Delta.Content, "Hello, world!")
	}
}

func TestConvertLineSystem(t *testing.T) {
	line := `{"type":"system","message":{"role":"system","content":"init"}}`
	result := ConvertLine(line, "chat-123")

	if !result.Skip {
		t.Error("expected Skip for system line")
	}
	if result.Chunk != nil {
		t.Error("expected nil Chunk for system line")
	}
	if result.Error != nil {
		t.Errorf("unexpected error: %v", result.Error)
	}
}

func TestConvertLineUser(t *testing.T) {
	line := `{"type":"user","message":{"role":"user","content":"hello"}}`
	result := ConvertLine(line, "chat-123")

	if !result.Skip {
		t.Error("expected Skip for user line")
	}
	if result.Chunk != nil {
		t.Error("expected nil Chunk for user line")
	}
	if result.Error != nil {
		t.Errorf("unexpected error: %v", result.Error)
	}
}

func TestConvertLineResultSuccess(t *testing.T) {
	line := `{"type":"result","subtype":"success","result":"done","usage":{"inputTokens":100,"outputTokens":50}}`
	result := ConvertLine(line, "chat-123")

	if !result.Done {
		t.Error("expected Done")
	}
	if result.Skip {
		t.Error("expected not Skip")
	}
	if result.Error != nil {
		t.Fatalf("unexpected error: %v", result.Error)
	}
	if result.Usage == nil {
		t.Fatal("expected Usage, got nil")
	}
	if result.Usage.InputTokens != 100 {
		t.Errorf("Usage.InputTokens = %d, want 100", result.Usage.InputTokens)
	}
	if result.Usage.OutputTokens != 50 {
		t.Errorf("Usage.OutputTokens = %d, want 50", result.Usage.OutputTokens)
	}
}

func TestConvertLineResultError(t *testing.T) {
	line := `{"type":"result","is_error":true,"result":"something went wrong"}`
	result := ConvertLine(line, "chat-123")

	if result.Error == nil {
		t.Fatal("expected error, got nil")
	}
	if !strings.Contains(result.Error.Error(), "something went wrong") {
		t.Errorf("error = %q, want to contain %q", result.Error.Error(), "something went wrong")
	}
	if result.Done {
		t.Error("expected not Done for error")
	}
}

func TestConvertLineEmpty(t *testing.T) {
	tests := []string{"", "   ", "\n", "  \n  "}
	for _, line := range tests {
		result := ConvertLine(line, "chat-123")
		if !result.Skip {
			t.Errorf("expected Skip for empty/whitespace line %q", line)
		}
		if result.Error != nil {
			t.Errorf("unexpected error for empty line: %v", result.Error)
		}
	}
}

func TestConvertLineInvalidJSON(t *testing.T) {
	line := `{"type":"assistant", invalid json}`
	result := ConvertLine(line, "chat-123")

	if result.Error == nil {
		t.Fatal("expected error for invalid JSON, got nil")
	}
	if !strings.Contains(result.Error.Error(), "unmarshal error") {
		t.Errorf("error = %q, want to contain %q", result.Error.Error(), "unmarshal error")
	}
}

func TestExtractContent(t *testing.T) {
	t.Run("nil message", func(t *testing.T) {
		result := ExtractContent(nil)
		if result != "" {
			t.Errorf("ExtractContent(nil) = %q, want empty", result)
		}
	})

	t.Run("single content", func(t *testing.T) {
		msg := &CursorMessage{
			Role: "assistant",
			Content: []CursorContent{
				{Type: "text", Text: "Hello"},
			},
		}
		result := ExtractContent(msg)
		if result != "Hello" {
			t.Errorf("ExtractContent() = %q, want %q", result, "Hello")
		}
	})

	t.Run("multiple content", func(t *testing.T) {
		msg := &CursorMessage{
			Role: "assistant",
			Content: []CursorContent{
				{Type: "text", Text: "Hello"},
				{Type: "text", Text: ", "},
				{Type: "text", Text: "world!"},
			},
		}
		result := ExtractContent(msg)
		if result != "Hello, world!" {
			t.Errorf("ExtractContent() = %q, want %q", result, "Hello, world!")
		}
	})

	t.Run("empty content", func(t *testing.T) {
		msg := &CursorMessage{
			Role:    "assistant",
			Content: []CursorContent{},
		}
		result := ExtractContent(msg)
		if result != "" {
			t.Errorf("ExtractContent() = %q, want empty", result)
		}
	})
}

func TestStreamParser_OnlyEmitsNewDeltaFromAccumulatedAssistantMessages(t *testing.T) {
	parser := NewStreamParser("chat-123")

	first := parser.Parse(`{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"Hel"}]}}`)
	if first.Error != nil {
		t.Fatalf("unexpected error on first chunk: %v", first.Error)
	}
	if first.Chunk == nil || first.Chunk.Choices[0].Delta.Content == nil {
		t.Fatal("expected first chunk content")
	}
	if got := *first.Chunk.Choices[0].Delta.Content; got != "Hel" {
		t.Fatalf("first delta = %q, want %q", got, "Hel")
	}

	second := parser.Parse(`{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"Hello"}]}}`)
	if second.Error != nil {
		t.Fatalf("unexpected error on second chunk: %v", second.Error)
	}
	if second.Chunk == nil || second.Chunk.Choices[0].Delta.Content == nil {
		t.Fatal("expected second chunk content")
	}
	if got := *second.Chunk.Choices[0].Delta.Content; got != "lo" {
		t.Fatalf("second delta = %q, want %q", got, "lo")
	}
}

func TestStreamParser_SkipsFinalDuplicateAssistantMessage(t *testing.T) {
	parser := NewStreamParser("chat-123")

	first := parser.Parse(`{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"Hello"}]}}`)
	if first.Skip || first.Error != nil || first.Chunk == nil {
		t.Fatalf("expected first assistant chunk, got %+v", first)
	}

	duplicate := parser.Parse(`{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"Hello"}]}}`)
	if !duplicate.Skip {
		t.Fatalf("expected duplicate assistant message to be skipped, got %+v", duplicate)
	}
}

func TestStreamParser_ResultIncludesUsage(t *testing.T) {
	parser := NewStreamParser("chat-123")

	result := parser.Parse(`{"type":"result","subtype":"success","usage":{"inputTokens":10,"outputTokens":4}}`)
	if !result.Done {
		t.Fatal("expected result.Done")
	}
	if result.Usage == nil {
		t.Fatal("expected usage")
	}
	if result.Usage.InputTokens != 10 || result.Usage.OutputTokens != 4 {
		t.Fatalf("unexpected usage: %+v", result.Usage)
	}
}

func TestStreamParser_CanReconstructFinalContentFromIncrementalAssistantMessages(t *testing.T) {
	parser := NewStreamParser("chat-123")

	lines := []string{
		`{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"你"}]}}`,
		`{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"你好"}]}}`,
		`{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"你好，世界"}]}}`,
	}

	var content strings.Builder
	for i, line := range lines {
		result := parser.Parse(line)
		if result.Error != nil {
			t.Fatalf("line %d unexpected error: %v", i, result.Error)
		}
		if result.Skip {
			continue
		}
		if result.Chunk == nil || result.Chunk.Choices[0].Delta.Content == nil {
			t.Fatalf("line %d expected chunk content, got %+v", i, result)
		}
		content.WriteString(*result.Chunk.Choices[0].Delta.Content)
	}

	if got := content.String(); got != "你好，世界" {
		t.Fatalf("reconstructed content = %q, want %q", got, "你好，世界")
	}
}

func TestStreamParser_RawTextFallbackSkipsExactDuplicates(t *testing.T) {
	parser := NewStreamParser("chat-123")

	first := parser.ParseRawText("plain chunk")
	if first.Skip || first.Chunk == nil || first.Chunk.Choices[0].Delta.Content == nil {
		t.Fatalf("expected raw text chunk, got %+v", first)
	}

	duplicate := parser.ParseRawText("plain chunk")
	if !duplicate.Skip {
		t.Fatalf("expected duplicate raw text to be skipped, got %+v", duplicate)
	}
}

func TestStreamParser_IncrementalFragmentsAccumulateAndSkipFinalDuplicate(t *testing.T) {
	parser := NewStreamParser("chat-123")

	fragments := []string{"你", "好，", "世", "界！"}
	var got strings.Builder
	for i, fr := range fragments {
		line := fmt.Sprintf(`{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":%q}]}}`, fr)
		res := parser.Parse(line)
		if res.Skip || res.Error != nil || res.Chunk == nil {
			t.Fatalf("fragment %d: expected delta chunk, got %+v", i, res)
		}
		got.WriteString(*res.Chunk.Choices[0].Delta.Content)
	}

	if got.String() != "你好，世界！" {
		t.Fatalf("reconstructed = %q, want 你好，世界！", got.String())
	}

	final := parser.Parse(`{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"你好，世界！"}]}}`)
	if !final.Skip {
		t.Fatalf("expected final duplicate cumulative message to be skipped, got %+v", final)
	}
}

func TestNewStreamParser_DoesNotExistYet(t *testing.T) {
	_ = fmt.Sprintf
}
