package fileutil

import (
	"path/filepath"
	"strings"

	"github.com/bmatcuk/doublestar/v4"
)

// GlobSet matches slash-normalized paths against include and exclude patterns.
// It centralizes doublestar semantics for callers that need consistent
// include/exclude filtering without shell expansion.
type GlobSet struct {
	include []string
	exclude []string
}

func NewGlobSet(include, exclude []string) (GlobSet, error) {
	set := GlobSet{
		include: normalizeGlobPatterns(include),
		exclude: normalizeGlobPatterns(exclude),
	}
	for _, pattern := range append(append([]string(nil), set.include...), set.exclude...) {
		if _, err := doublestar.Match(pattern, ""); err != nil {
			return GlobSet{}, err
		}
	}
	return set, nil
}

func (s GlobSet) Match(path string) bool {
	path = NormalizeSlashPath(path)
	included := len(s.include) == 0
	for _, pattern := range s.include {
		if MatchSlashGlob(path, pattern) {
			included = true
			break
		}
	}
	if !included {
		return false
	}
	for _, pattern := range s.exclude {
		if MatchSlashGlob(path, pattern) {
			return false
		}
	}
	return true
}

func MatchSlashGlob(path, pattern string) bool {
	path = NormalizeSlashPath(path)
	pattern = NormalizeSlashPath(pattern)
	if matched, _ := doublestar.Match(pattern, path); matched {
		return true
	}
	if after, ok := strings.CutPrefix(pattern, "**/"); ok {
		matched, _ := doublestar.Match(after, path)
		return matched
	}
	return false
}

func NormalizeSlashPath(path string) string {
	return filepath.ToSlash(filepath.Clean(path))
}

func normalizeGlobPatterns(patterns []string) []string {
	out := make([]string, 0, len(patterns))
	for _, pattern := range patterns {
		pattern = strings.TrimSpace(pattern)
		if pattern == "" {
			continue
		}
		out = append(out, NormalizeSlashPath(pattern))
	}
	return out
}
