package testmark_test

import (
	"io/fs"
	"testing"

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

const filesystemDoc = `# Test Document

[testmark]:# (file1.txt)
` + "```\nContent of file 1\n```" + `

[testmark]:# (dir1/file2.txt)
` + "```\nContent of file 2\n```" + `

[testmark]:# (dir1/dir2/file3.txt)
` + "```\nContent of file 3\n```" + `
`

func TestFilesystem(t *testing.T) {
	doc := testmark.MustParse(filesystemDoc)
	filesys := doc.FS()

	// Test opening root directory
	rootFile, err := filesys.Open(".")
	if err != nil {
		t.Fatalf("failed to open root: %v", err)
	}
	defer rootFile.Close()

	// Test that root is a directory
	stat, err := rootFile.Stat()
	if err != nil {
		t.Fatalf("failed to stat root: %v", err)
	}
	if !stat.IsDir() {
		t.Error("root should be a directory")
	}

	// Test reading root directory
	if readDirFile, ok := rootFile.(fs.ReadDirFile); ok {
		entries, err := readDirFile.ReadDir(-1)
		if err != nil {
			t.Fatalf("failed to read root directory: %v", err)
		}
		if len(entries) != 2 { // file1.txt and dir1
			t.Errorf("expected 2 entries in root, got %d", len(entries))
		}
	} else {
		t.Error("root file should implement ReadDirFile")
	}

	// Test opening a file
	file1, err := filesys.Open("file1.txt")
	if err != nil {
		t.Fatalf("failed to open file1.txt: %v", err)
	}
	defer file1.Close()

	// Test reading file content
	content := make([]byte, 100)
	n, err := file1.Read(content)
	if err != nil {
		t.Fatalf("failed to read file1.txt: %v", err)
	}
	if string(content[:n]) != "Content of file 1\n" {
		t.Errorf("unexpected content: got %q", string(content[:n]))
	}

	// Test opening nested file
	file3, err := filesys.Open("dir1/dir2/file3.txt")
	if err != nil {
		t.Fatalf("failed to open dir1/dir2/file3.txt: %v", err)
	}
	defer file3.Close()

	content = make([]byte, 100)
	n, err = file3.Read(content)
	if err != nil {
		t.Fatalf("failed to read dir1/dir2/file3.txt: %v", err)
	}
	if string(content[:n]) != "Content of file 3\n" {
		t.Errorf("unexpected content: got %q", string(content[:n]))
	}

	// Test opening non-existent file
	_, err = filesys.Open("nonexistent.txt")
	if err == nil {
		t.Error("expected error opening nonexistent file")
	}
}

func TestFilesystemWalkDir(t *testing.T) {
	doc := testmark.MustParse(filesystemDoc)
	filesys := doc.FS()

	// Test walking the filesystem
	var paths []string
	err := fs.WalkDir(filesys, ".", func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}
		paths = append(paths, path)
		return nil
	})
	if err != nil {
		t.Fatalf("failed to walk filesystem: %v", err)
	}

	// Check that we found expected paths
	expectedPaths := []string{".", "dir1", "dir1/dir2", "dir1/dir2/file3.txt", "dir1/file2.txt", "file1.txt"}
	if len(paths) != len(expectedPaths) {
		t.Errorf("expected %d paths, got %d: %v", len(expectedPaths), len(paths), paths)
	}
}

func TestFilesystemGlob(t *testing.T) {
	doc := testmark.MustParse(filesystemDoc)
	filesys := doc.FS()

	// Test globbing for all .txt files
	matches, err := fs.Glob(filesys, "*.txt")
	if err != nil {
		t.Fatalf("failed to glob *.txt: %v", err)
	}
	if len(matches) != 1 || matches[0] != "file1.txt" {
		t.Errorf("expected [file1.txt], got %v", matches)
	}

	// Test recursive globbing
	matches, err = fs.Glob(filesys, "**/*.txt")
	if err != nil {
		t.Fatalf("failed to glob **/*.txt: %v", err)
	}
	// Note: **/ glob may not work with all implementations, so we'll test dir1/*.txt
	matches, err = fs.Glob(filesys, "dir1/*.txt")
	if err != nil {
		t.Fatalf("failed to glob dir1/*.txt: %v", err)
	}
	if len(matches) != 1 || matches[0] != "dir1/file2.txt" {
		t.Errorf("expected [dir1/file2.txt], got %v", matches)
	}
}
