package main

import (
	"testing"

	ignore "github.com/sabhiram/go-gitignore"
)

func TestGitignore_SimplePatterns(t *testing.T) {
	tests := []struct {
		name     string
		patterns []string
		path     string
		expected bool
	}{
		{"exact file", []string{"foo.txt"}, "foo.txt", true},
		{"exact file no match", []string{"foo.txt"}, "bar.txt", false},
		{"wildcard extension", []string{"*.log"}, "debug.log", true},
		{"wildcard no match", []string{"*.log"}, "debug.txt", false},
		{"directory pattern with content", []string{"build/"}, "build/output.o", true},
		{"leading slash anchored", []string{"/foo"}, "foo", true},
		{"leading slash anchored no match", []string{"/foo"}, "bar/foo", false},
		{"comment line ignored", []string{"# this is a comment"}, "foo", false},
		{"empty line ignored", []string{""}, "foo", false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gi := ignore.CompileIgnoreLines(tt.patterns...)
			result := gi.MatchesPath(tt.path)
			if result != tt.expected {
				t.Errorf("MatchesPath(%q) = %v, want %v", tt.path, result, tt.expected)
			}
		})
	}
}

func TestGitignore_Negation(t *testing.T) {
	gi := ignore.CompileIgnoreLines("*.log", "!important.log")

	tests := []struct {
		path     string
		expected bool
	}{
		{"debug.log", true},
		{"important.log", false},
		{"test.log", true},
		{"readme.txt", false},
	}

	for _, tt := range tests {
		t.Run(tt.path, func(t *testing.T) {
			result := gi.MatchesPath(tt.path)
			if result != tt.expected {
				t.Errorf("MatchesPath(%q) = %v, want %v", tt.path, result, tt.expected)
			}
		})
	}
}

func TestGitignore_DoubleStar(t *testing.T) {
	tests := []struct {
		name     string
		patterns []string
		path     string
		expected bool
	}{
		{"double star prefix", []string{"**/temp"}, "temp", true},
		{"double star prefix nested", []string{"**/temp"}, "foo/temp", true},
		{"double star prefix deep nested", []string{"**/temp"}, "foo/bar/baz/temp", true},
		{"double star suffix", []string{"temp/**"}, "temp/file.txt", true},
		{"double star suffix deep", []string{"temp/**"}, "temp/sub/file.txt", true},
		{"double star middle", []string{"foo/**/bar"}, "foo/bar", true},
		{"double star middle nested", []string{"foo/**/bar"}, "foo/x/y/bar", true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gi := ignore.CompileIgnoreLines(tt.patterns...)
			result := gi.MatchesPath(tt.path)
			if result != tt.expected {
				t.Errorf("MatchesPath(%q) = %v, want %v", tt.path, result, tt.expected)
			}
		})
	}
}

func TestGitignore_QuestionMark(t *testing.T) {
	// NOTE: go-gitignore does not support ? wildcard
	// This test documents the limitation
	gi := ignore.CompileIgnoreLines("?.txt")

	result := gi.MatchesPath("a.txt")
	if result {
		t.Log("go-gitignore now supports ? wildcard - update docs")
	} else {
		t.Log("go-gitignore does NOT support ? wildcard - known limitation")
	}
}

func TestGitignore_BracketRange(t *testing.T) {
	gi := ignore.CompileIgnoreLines("[abc].txt")

	tests := []struct {
		path     string
		expected bool
	}{
		{"a.txt", true},
		{"b.txt", true},
		{"c.txt", true},
		{"d.txt", false},
	}

	for _, tt := range tests {
		t.Run(tt.path, func(t *testing.T) {
			result := gi.MatchesPath(tt.path)
			if result != tt.expected {
				t.Errorf("MatchesPath(%q) = %v, want %v", tt.path, result, tt.expected)
			}
		})
	}
}

func TestGitignore_NestedGitignore(t *testing.T) {
	rules := map[string][]string{
		".gitignore":             {"*.log"},
		"subdir/.gitignore":      {"*.tmp"},
		"subdir/deep/.gitignore": {"*.bak"},
	}

	gi := ignore.CompileIgnoreLines(rules[".gitignore"]...)

	tests := []struct {
		name     string
		path     string
		expected bool
	}{
		{"root log", "debug.log", true},
		{"root txt", "readme.txt", false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := gi.MatchesPath(tt.path)
			if result != tt.expected {
				t.Errorf("MatchesPath(%q) = %v, want %v", tt.path, result, tt.expected)
			}
		})
	}

	// Nested gitignore needs separate handling per directory
	subGi := ignore.CompileIgnoreLines(rules["subdir/.gitignore"]...)
	if subGi.MatchesPath("file.tmp") != true {
		t.Error("subdir gitignore should match *.tmp")
	}
}

func TestGitignore_CombinedPatterns(t *testing.T) {
	gi := ignore.CompileIgnoreLines(
		"*.o",
		"*.a",
		"build/",
		"!lib/special.o",
	)

	tests := []struct {
		path     string
		expected bool
	}{
		{"foo.o", true},
		{"lib.a", true},
		{"build/output.o", true},
		{"lib/special.o", false},
		{"src/main.c", false},
	}

	for _, tt := range tests {
		t.Run(tt.path, func(t *testing.T) {
			result := gi.MatchesPath(tt.path)
			if result != tt.expected {
				t.Errorf("MatchesPath(%q) = %v, want %v", tt.path, result, tt.expected)
			}
		})
	}
}

func TestGitignore_HashInFilename(t *testing.T) {
	gi := ignore.CompileIgnoreLines("*.txt")

	tests := []struct {
		path     string
		expected bool
	}{
		{"file#1.txt", true},
		{"file#2.txt", true},
		{"readme.md", false},
	}

	for _, tt := range tests {
		t.Run(tt.path, func(t *testing.T) {
			result := gi.MatchesPath(tt.path)
			if result != tt.expected {
				t.Errorf("MatchesPath(%q) = %v, want %v", tt.path, result, tt.expected)
			}
		})
	}
}

func TestGitignore_TrailingSpace(t *testing.T) {
	// NOTE: go-gitignore does not handle trailing spaces in patterns
	// This test documents the limitation
	gi := ignore.CompileIgnoreLines("foo.txt   ")

	result := gi.MatchesPath("foo.txt")
	if result {
		t.Log("go-gitignore handles trailing spaces correctly")
	} else {
		t.Log("go-gitignore does NOT handle trailing spaces - known limitation")
	}
}
