package parser

import (
	"encoding/json"
	"testing"
)

func makeAssistantLine(text string) string {
	obj := map[string]interface{}{
		"type": "assistant",
		"message": map[string]interface{}{
			"content": []map[string]interface{}{
				{"type": "text", "text": text},
			},
		},
	}
	b, _ := json.Marshal(obj)
	return string(b)
}

func makeResultLine() string {
	b, _ := json.Marshal(map[string]string{"type": "result", "subtype": "success"})
	return string(b)
}

func TestStreamParserFragmentMode(t *testing.T) {
	// cursor --stream-partial-output 模式：每個訊息是獨立 token fragment
	var texts []string
	p := CreateStreamParser(
		func(text string) { texts = append(texts, text) },
		func() {},
	)

	p.Parse(makeAssistantLine("你"))
	p.Parse(makeAssistantLine("好！有"))
	p.Parse(makeAssistantLine("什"))
	p.Parse(makeAssistantLine("麼"))

	if len(texts) != 4 {
		t.Fatalf("expected 4 fragments, got %d: %v", len(texts), texts)
	}
	if texts[0] != "你" || texts[1] != "好！有" || texts[2] != "什" || texts[3] != "麼" {
		t.Fatalf("unexpected texts: %v", texts)
	}
}

func TestStreamParserDeduplicatesFinalFullText(t *testing.T) {
	// 最後一個訊息是完整的累積文字，應被跳過（去重）
	var texts []string
	p := CreateStreamParser(
		func(text string) { texts = append(texts, text) },
		func() {},
	)

	p.Parse(makeAssistantLine("Hello"))
	p.Parse(makeAssistantLine(" world"))
	// 最後一個是完整累積文字，應被去重
	p.Parse(makeAssistantLine("Hello world"))

	if len(texts) != 2 {
		t.Fatalf("expected 2 fragments (final full text deduplicated), got %d: %v", len(texts), texts)
	}
	if texts[0] != "Hello" || texts[1] != " world" {
		t.Fatalf("unexpected texts: %v", texts)
	}
}

func TestStreamParserCallsOnDone(t *testing.T) {
	var texts []string
	doneCount := 0
	p := CreateStreamParser(
		func(text string) { texts = append(texts, text) },
		func() { doneCount++ },
	)

	p.Parse(makeResultLine())
	if doneCount != 1 {
		t.Fatalf("expected onDone called once, got %d", doneCount)
	}
	if len(texts) != 0 {
		t.Fatalf("expected no text, got %v", texts)
	}
}

func TestStreamParserIgnoresLinesAfterDone(t *testing.T) {
	var texts []string
	doneCount := 0
	p := CreateStreamParser(
		func(text string) { texts = append(texts, text) },
		func() { doneCount++ },
	)

	p.Parse(makeResultLine())
	p.Parse(makeAssistantLine("late"))
	if len(texts) != 0 {
		t.Fatalf("expected no text after done, got %v", texts)
	}
	if doneCount != 1 {
		t.Fatalf("expected onDone called once, got %d", doneCount)
	}
}

func TestStreamParserIgnoresNonAssistantLines(t *testing.T) {
	var texts []string
	p := CreateStreamParser(
		func(text string) { texts = append(texts, text) },
		func() {},
	)

	b1, _ := json.Marshal(map[string]interface{}{"type": "user", "message": map[string]interface{}{}})
	p.Parse(string(b1))
	b2, _ := json.Marshal(map[string]interface{}{
		"type": "assistant",
		"message": map[string]interface{}{"content": []interface{}{}},
	})
	p.Parse(string(b2))
	p.Parse(`{"type":"assistant","message":{"content":[{"type":"code","text":"x"}]}}`)

	if len(texts) != 0 {
		t.Fatalf("expected no texts, got %v", texts)
	}
}

func TestStreamParserIgnoresParseErrors(t *testing.T) {
	var texts []string
	doneCount := 0
	p := CreateStreamParser(
		func(text string) { texts = append(texts, text) },
		func() { doneCount++ },
	)

	p.Parse("not json")
	p.Parse("{")
	p.Parse("")

	if len(texts) != 0 || doneCount != 0 {
		t.Fatalf("expected nothing, got texts=%v done=%d", texts, doneCount)
	}
}

func TestStreamParserJoinsMultipleTextParts(t *testing.T) {
	var texts []string
	p := CreateStreamParser(
		func(text string) { texts = append(texts, text) },
		func() {},
	)

	obj := map[string]interface{}{
		"type": "assistant",
		"message": map[string]interface{}{
			"content": []map[string]interface{}{
				{"type": "text", "text": "Hello"},
				{"type": "text", "text": " "},
				{"type": "text", "text": "world"},
			},
		},
	}
	b, _ := json.Marshal(obj)
	p.Parse(string(b))

	if len(texts) != 1 || texts[0] != "Hello world" {
		t.Fatalf("expected ['Hello world'], got %v", texts)
	}
}

func TestStreamParserFlushTriggersDone(t *testing.T) {
	var texts []string
	doneCount := 0
	p := CreateStreamParser(
		func(text string) { texts = append(texts, text) },
		func() { doneCount++ },
	)

	p.Parse(makeAssistantLine("Hello"))
	// agent 結束但沒有 result/success，手動 flush
	p.Flush()
	if doneCount != 1 {
		t.Fatalf("expected onDone called once after Flush, got %d", doneCount)
	}
	// 再 flush 不應重複觸發
	p.Flush()
	if doneCount != 1 {
		t.Fatalf("expected onDone called only once, got %d", doneCount)
	}
}

func TestStreamParserFlushAfterDoneIsNoop(t *testing.T) {
	doneCount := 0
	p := CreateStreamParser(
		func(text string) {},
		func() { doneCount++ },
	)

	p.Parse(makeResultLine())
	p.Flush()
	if doneCount != 1 {
		t.Fatalf("expected onDone called once, got %d", doneCount)
	}
}

func makeThinkingLine(thinking string) string {
	obj := map[string]interface{}{
		"type": "assistant",
		"message": map[string]interface{}{
			"content": []map[string]interface{}{
				{"type": "thinking", "thinking": thinking},
			},
		},
	}
	b, _ := json.Marshal(obj)
	return string(b)
}

func makeThinkingAndTextLine(thinking, text string) string {
	obj := map[string]interface{}{
		"type": "assistant",
		"message": map[string]interface{}{
			"content": []map[string]interface{}{
				{"type": "thinking", "thinking": thinking},
				{"type": "text", "text": text},
			},
		},
	}
	b, _ := json.Marshal(obj)
	return string(b)
}

func TestStreamParserWithThinkingCallsOnThinking(t *testing.T) {
	var texts []string
	var thinkings []string
	p := CreateStreamParserWithThinking(
		func(text string) { texts = append(texts, text) },
		func(thinking string) { thinkings = append(thinkings, thinking) },
		func() {},
	)

	p.Parse(makeThinkingLine("思考中..."))
	p.Parse(makeAssistantLine("回答"))

	if len(thinkings) != 1 || thinkings[0] != "思考中..." {
		t.Fatalf("expected thinkings=['思考中...'], got %v", thinkings)
	}
	if len(texts) != 1 || texts[0] != "回答" {
		t.Fatalf("expected texts=['回答'], got %v", texts)
	}
}

func TestStreamParserWithThinkingNilOnThinkingIgnoresThinking(t *testing.T) {
	var texts []string
	p := CreateStreamParserWithThinking(
		func(text string) { texts = append(texts, text) },
		nil,
		func() {},
	)

	p.Parse(makeThinkingLine("忽略的思考"))
	p.Parse(makeAssistantLine("文字"))

	if len(texts) != 1 || texts[0] != "文字" {
		t.Fatalf("expected texts=['文字'], got %v", texts)
	}
}

func TestStreamParserWithThinkingDeduplication(t *testing.T) {
	var thinkings []string
	p := CreateStreamParserWithThinking(
		func(text string) {},
		func(thinking string) { thinkings = append(thinkings, thinking) },
		func() {},
	)

	p.Parse(makeThinkingLine("A"))
	p.Parse(makeThinkingLine("B"))
	// 重複的完整思考，應被跳過
	p.Parse(makeThinkingLine("AB"))

	if len(thinkings) != 2 || thinkings[0] != "A" || thinkings[1] != "B" {
		t.Fatalf("expected thinkings=['A','B'], got %v", thinkings)
	}
}

// TestStreamParserThinkingDuplicateButTextStillEmitted 驗證 bug 修復：
// 當 thinking 重複（去重跳過）但同一行有 text 時，text 仍必須輸出。
func TestStreamParserThinkingDuplicateButTextStillEmitted(t *testing.T) {
	var texts []string
	var thinkings []string
	p := CreateStreamParserWithThinking(
		func(text string) { texts = append(texts, text) },
		func(thinking string) { thinkings = append(thinkings, thinking) },
		func() {},
	)

	// 第一行：thinking="思考中" + text（thinking 為新增，兩者都應輸出）
	p.Parse(makeThinkingAndTextLine("思考中", "第一段"))
	// 第二行：thinking 與上一行相同（去重），但 text 是新的，text 仍應輸出
	p.Parse(makeThinkingAndTextLine("思考中", "第二段"))

	if len(thinkings) != 1 || thinkings[0] != "思考中" {
		t.Fatalf("expected thinkings=['思考中'], got %v", thinkings)
	}
	if len(texts) != 2 || texts[0] != "第一段" || texts[1] != "第二段" {
		t.Fatalf("expected texts=['第一段','第二段'], got %v", texts)
	}
}
