package run

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

func readFixture(t *testing.T, name string) []byte {
	t.Helper()
	b, err := os.ReadFile(filepath.Join("testdata", name))
	if err != nil {
		t.Fatalf("read fixture %s: %v", name, err)
	}
	return b
}

func TestParseGoTestJSON(t *testing.T) {
	sum, err := parseGoTestJSON(readFixture(t, "gotest.jsonl"))
	if err != nil {
		t.Fatalf("parse: %v", err)
	}
	if sum.Framework != "gotest" {
		t.Fatalf("framework = %q", sum.Framework)
	}
	if sum.Passed != 1 || sum.Failed != 1 || sum.Skipped != 1 || sum.Total != 3 {
		t.Fatalf("counts p=%d f=%d s=%d t=%d", sum.Passed, sum.Failed, sum.Skipped, sum.Total)
	}
	if len(sum.Failures) != 1 || sum.Failures[0].Name != "TestFail" || sum.Failures[0].Package != "example/pkg" {
		t.Fatalf("failures = %+v", sum.Failures)
	}
	if sum.Failures[0].Message == "" {
		t.Fatal("failure message should not be empty")
	}
}

func TestParseJUnitXML(t *testing.T) {
	sum, err := parseJUnitXML(readFixture(t, "junit.xml"))
	if err != nil {
		t.Fatalf("parse: %v", err)
	}
	if sum.Framework != "junit" {
		t.Fatalf("framework = %q", sum.Framework)
	}
	if sum.Passed != 1 || sum.Failed != 1 || sum.Skipped != 1 || sum.Total != 3 {
		t.Fatalf("counts p=%d f=%d s=%d t=%d", sum.Passed, sum.Failed, sum.Skipped, sum.Total)
	}
	if len(sum.Failures) != 1 || sum.Failures[0].Name != "testFails" {
		t.Fatalf("failures = %+v", sum.Failures)
	}
	if sum.Failures[0].Message != "expected true but was false" {
		t.Fatalf("failure message = %q", sum.Failures[0].Message)
	}
}

func TestParseVitestJSON(t *testing.T) {
	sum, err := parseVitestJSON(readFixture(t, "vitest.json"))
	if err != nil {
		t.Fatalf("parse: %v", err)
	}
	if sum.Framework != "vitest" {
		t.Fatalf("framework = %q", sum.Framework)
	}
	if sum.Passed != 1 || sum.Failed != 1 || sum.Skipped != 1 || sum.Total != 3 {
		t.Fatalf("counts p=%d f=%d s=%d t=%d", sum.Passed, sum.Failed, sum.Skipped, sum.Total)
	}
	if len(sum.Failures) != 1 || sum.Failures[0].Name != "sum > subtracts" {
		t.Fatalf("failures = %+v", sum.Failures)
	}
	if sum.Failures[0].Message != "expected 1 to be 2" {
		t.Fatalf("failure message = %q", sum.Failures[0].Message)
	}
}

// TestParseTestOutput_Auto 验证 auto 嗅探把各格式路由到正确解析器。
func TestParseTestOutput_Auto(t *testing.T) {
	cases := []struct {
		name    string
		fixture string
		want    string
	}{
		{"gotest", "gotest.jsonl", "gotest"},
		{"junit", "junit.xml", "junit"},
		{"vitest", "vitest.json", "vitest"},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			sum, err := parseTestOutput("auto", string(readFixture(t, tc.fixture)), "")
			if err != nil {
				t.Fatalf("parse: %v", err)
			}
			if sum.Framework != tc.want {
				t.Fatalf("framework = %q, want %q", sum.Framework, tc.want)
			}
		})
	}
}

// TestParseTestOutput_Malformed 验证解析失败返回 error（由 RunTests 折成 ParseError），
// 永不 panic。
func TestParseTestOutput_Malformed(t *testing.T) {
	cases := []struct {
		format string
		in     string
	}{
		{"gotest", "not json at all"},
		{"junit", "<broken><xml"},
		{"vitest", "{not valid"},
		{"auto", ""},
		{"auto", "random text with no structure"},
	}
	for _, tc := range cases {
		_, err := parseTestOutput(tc.format, tc.in, "")
		if err == nil {
			t.Fatalf("format %q input %q: expected parse error, got nil", tc.format, tc.in)
		}
	}
}

// TestParseGoTestJSON_StderrFallback 验证 stdout 为空时退回 stderr。
func TestParseTestOutput_StderrFallback(t *testing.T) {
	sum, err := parseTestOutput("auto", "", string(readFixture(t, "gotest.jsonl")))
	if err != nil {
		t.Fatalf("parse: %v", err)
	}
	if sum.Framework != "gotest" || sum.Total != 3 {
		t.Fatalf("got %+v", sum)
	}
}
