package testmark_test

import (
	"strings"
	"testing"
	"testing/fstest"

	testmark "github.com/warpfork/go-testmark/v2"
)

const helpersTestDoc = `# Helper Test Document

[testmark]:# (config/database)
` + "```json\n{\"host\": \"localhost\"}\n```" + `

[testmark]:# (config/redis)
` + "```yaml\nhost: redis-server\n```" + `

[testmark]:# (test/unit)
` + "```go\nfunc TestSomething(t *testing.T) {}\n```" + `

[testmark]:# (test/integration)
` + "```go\nfunc TestIntegration(t *testing.T) {}\n```" + `
`

func TestDocumentHelpers(t *testing.T) {
	doc := testmark.MustParse(helpersTestDoc)

	// Test AllHunks
	allHunks := doc.AllHunks()
	if len(allHunks) != 4 {
		t.Errorf("expected 4 hunks, got %d", len(allHunks))
	}

	// Test HasHunk
	if !doc.HasHunk("config/database") {
		t.Error("should have config/database hunk")
	}
	if doc.HasHunk("nonexistent") {
		t.Error("should not have nonexistent hunk")
	}

	// Test MustHunkByName
	hunk := doc.MustHunkByName("config/database")
	if hunk.Name() != "config/database" {
		t.Error("MustHunkByName returned wrong hunk")
	}

	// Test HunkNames
	names := doc.HunkNames()
	expectedNames := []string{"config/database", "config/redis", "test/unit", "test/integration"}
	if len(names) != len(expectedNames) {
		t.Errorf("expected %d names, got %d", len(expectedNames), len(names))
	}

	// Test HunksByPrefix
	configHunks := doc.HunksByPrefix("config/")
	if len(configHunks) != 2 {
		t.Errorf("expected 2 config hunks, got %d", len(configHunks))
	}

	// Test HunksBySuffix
	testHunks := doc.HunksBySuffix("unit")
	if len(testHunks) != 1 {
		t.Errorf("expected 1 unit test hunk, got %d", len(testHunks))
	}

	// Test HunksByInfoString
	goHunks := doc.HunksByInfoString("go")
	if len(goHunks) != 2 {
		t.Errorf("expected 2 go hunks, got %d", len(goHunks))
	}

	// Test ToMap
	hunkMap := doc.ToMap()
	if len(hunkMap) != 4 {
		t.Errorf("expected 4 entries in map, got %d", len(hunkMap))
	}
	if !strings.Contains(hunkMap["config/database"], "localhost") {
		t.Error("database config should contain localhost")
	}
}

func TestDocumentUpdateHelpers(t *testing.T) {
	doc := testmark.MustParse(helpersTestDoc)

	// Test UpdateHunk
	updated := doc.UpdateHunk("config/database", `{"host": "remote-server"}`)
	updatedHunk := updated.HunkByName("config/database")
	if !strings.Contains(updatedHunk.String(), "remote-server") {
		t.Error("hunk should be updated with new content")
	}

	// Test PatchFromMap
	updates := map[string]string{
		"config/database": `{"host": "db.example.com"}`,
		"config/redis":    "host: cache.example.com",
	}
	batchUpdated := doc.PatchFromMap(updates)
	dbHunk := batchUpdated.HunkByName("config/database")
	redisHunk := batchUpdated.HunkByName("config/redis")
	if !strings.Contains(dbHunk.String(), "db.example.com") {
		t.Error("database config should be updated")
	}
	if !strings.Contains(redisHunk.String(), "cache.example.com") {
		t.Error("redis config should be updated")
	}

	// Test Clone
	cloned := doc.Clone()
	if cloned == doc {
		t.Error("clone should not be the same instance")
	}
	if cloned.Count() != doc.Count() {
		t.Error("clone should have same number of hunks")
	}
}

func TestDirEntryHelpers(t *testing.T) {
	doc := testmark.MustParse(helpersTestDoc)
	root := doc.Root()

	// Test AllChildren
	allChildren := root.AllChildren()
	if len(allChildren) != 2 { // config and test directories
		t.Errorf("expected 2 children, got %d", len(allChildren))
	}

	// Test HasChild
	if !root.HasChild("config") {
		t.Error("root should have config child")
	}
	if root.HasChild("nonexistent") {
		t.Error("root should not have nonexistent child")
	}

	// Test ChildNames
	childNames := root.ChildNames()
	if len(childNames) != 2 {
		t.Errorf("expected 2 child names, got %d", len(childNames))
	}

	// Test FindByPath
	dbEntry := root.FindByPath("config/database")
	if dbEntry == nil {
		t.Error("should find config/database entry")
	}
	if dbEntry != nil && dbEntry.Hunk() == nil {
		t.Error("database entry should have a hunk")
	}

	nonexistent := root.FindByPath("config/nonexistent")
	if nonexistent != nil {
		t.Error("should not find nonexistent path")
	}
}

func TestFSHelpers(t *testing.T) {
	// Create a test filesystem
	fsys := fstest.MapFS{
		"test1.md": &fstest.MapFile{
			Data: []byte(helpersTestDoc),
		},
		"test2.md": &fstest.MapFile{
			Data: []byte("# No testmark content"),
		},
		"README.md": &fstest.MapFile{
			Data: []byte("# README\n\n[testmark]:# (example)\n" + "```\nexample content\n```\n"),
		},
	}

	// Test ParseFromFS
	doc, err := testmark.ParseFromFS(fsys, "test1.md")
	if err != nil {
		t.Fatalf("ParseFromFS failed: %v", err)
	}
	if doc.Count() != 4 {
		t.Errorf("expected 4 hunks, got %d", doc.Count())
	}

	// Test MustParseFromFS
	doc2 := testmark.MustParseFromFS(fsys, "README.md")
	if doc2.Count() != 1 {
		t.Errorf("expected 1 hunk, got %d", doc2.Count())
	}

	// Test ParseAllFromFS
	allDocs, err := testmark.ParseAllFromFS(fsys, "*.md")
	if err != nil {
		t.Fatalf("ParseAllFromFS failed: %v", err)
	}
	// Should find test1.md and README.md (test2.md has no testmark content)
	if len(allDocs) != 2 {
		t.Errorf("expected 2 documents, got %d", len(allDocs))
	}
}

func TestPatternMatching(t *testing.T) {
	doc := testmark.MustParse(helpersTestDoc)

	// Test HunksByPattern
	configHunks, err := doc.HunksByPattern("config/*")
	if err != nil {
		t.Fatalf("HunksByPattern failed: %v", err)
	}
	if len(configHunks) != 2 {
		t.Errorf("expected 2 config hunks, got %d", len(configHunks))
	}

	testHunks, err := doc.HunksByPattern("test/*")
	if err != nil {
		t.Fatalf("HunksByPattern failed: %v", err)
	}
	if len(testHunks) != 2 {
		t.Errorf("expected 2 test hunks, got %d", len(testHunks))
	}
}
