package wildcard

import (
	"errors"
	"strings"

	"github.com/gobwas/glob"
	"k8s.io/apimachinery/pkg/util/sets"
)

func ShouldExpandWildcards(includes []string, excludes []string, fromBackup bool) bool {
	// Only expand wildcards if this is being called from a backup request.
	// We don't want to expand wildcard patterns in restore request,
	// because restore needs the IncludeEverything function to
	// determine whether to restore cluster-scoped resources.
	// Expand wildcards causes the IncludeEverything function to return false,
	// which will cause restore to skip cluster-scoped resources.
	if !fromBackup {
		return false
	}

	// Empty includes is equivalent to * (match all) - don't expand
	if len(includes) == 0 {
		return false
	}

	wildcardFound := false
	for _, include := range includes {
		if containsWildcardPattern(include) {
			wildcardFound = true
		}
	}

	for _, exclude := range excludes {
		if containsWildcardPattern(exclude) {
			wildcardFound = true
		}
	}

	return wildcardFound
}

// containsWildcardPattern checks if a pattern contains any wildcard symbols
// Supported patterns: *, ?, [abc]
// Note: . and + are treated as literal characters (not wildcards)
// Note: ** and consecutive asterisks are NOT supported (will cause validation error)
func containsWildcardPattern(pattern string) bool {
	return strings.ContainsAny(pattern, "*?[")
}

func validateWildcardPatterns(patterns []string) error {
	for _, pattern := range patterns {
		if err := ValidateNamespaceName(pattern); err != nil {
			return err
		}
	}
	return nil
}

func ValidateNamespaceName(pattern string) error {
	// Check for invalid characters that are not supported in glob patterns
	if strings.ContainsAny(pattern, "|()!{},") {
		return errors.New("wildcard pattern contains unsupported characters: |, (, ), !, {, }, ,")
	}

	// Check for consecutive asterisks (2 or more)
	if strings.Contains(pattern, "**") {
		return errors.New("wildcard pattern contains consecutive asterisks (only single * allowed)")
	}

	// Check for malformed brace patterns
	if err := validateBracePatterns(pattern); err != nil {
		return err
	}

	return nil
}

// validateBracePatterns checks for malformed brace patterns like unclosed braces or empty braces
// Also validates bracket patterns [] for character classes
func validateBracePatterns(pattern string) error {
	bracketDepth := 0

	for i := 0; i < len(pattern); i++ {
		if pattern[i] == '[' {
			bracketStart := i
			bracketDepth++

			// Scan ahead to find the matching closing bracket and validate content
			for j := i + 1; j < len(pattern) && bracketDepth > 0; j++ {
				if pattern[j] == ']' {
					bracketDepth--
					if bracketDepth == 0 {
						// Found matching closing bracket - validate content
						content := pattern[bracketStart+1 : j]
						if content == "" {
							return errors.New("wildcard pattern contains empty bracket pattern '[]'")
						}
						// Skip to the closing bracket
						i = j
						break
					}
				}
			}

			// If we exited the loop without finding a match (bracketDepth > 0), bracket is unclosed
			if bracketDepth > 0 {
				return errors.New("wildcard pattern contains unclosed bracket '['")
			}

			// i is now positioned at the closing bracket; the outer loop will increment it
		} else if pattern[i] == ']' {
			// Found a closing bracket without a matching opening bracket
			return errors.New("wildcard pattern contains unmatched closing bracket ']'")
		}
	}

	return nil
}

func ExpandWildcards(activeNamespaces []string, includes []string, excludes []string) ([]string, []string, error) {
	expandedIncludes, err := expandWildcards(includes, activeNamespaces)
	if err != nil {
		return nil, nil, err
	}

	expandedExcludes, err := expandWildcards(excludes, activeNamespaces)
	if err != nil {
		return nil, nil, err
	}

	return expandedIncludes, expandedExcludes, nil
}

// expands wildcard patterns into a list of namespaces, while normally passing non-wildcard patterns
func expandWildcards(patterns []string, activeNamespaces []string) ([]string, error) {
	if len(patterns) == 0 {
		return nil, nil
	}

	// Validate patterns before processing
	if err := validateWildcardPatterns(patterns); err != nil {
		return nil, err
	}

	matchedSet := make(map[string]struct{})

	for _, pattern := range patterns {
		// If the pattern is a non-wildcard pattern, we can just add it to the result
		if !containsWildcardPattern(pattern) {
			matchedSet[pattern] = struct{}{}
			continue
		}

		// Compile glob pattern
		g, err := glob.Compile(pattern)
		if err != nil {
			return nil, err
		}

		// Match against all namespaces
		for _, ns := range activeNamespaces {
			if g.Match(ns) {
				matchedSet[ns] = struct{}{}
			}
		}
	}

	// Convert set to slice
	result := make([]string, 0, len(matchedSet))
	for ns := range matchedSet {
		result = append(result, ns)
	}

	return result, nil
}

// GetWildcardResult returns the final list of namespaces after applying wildcard include/exclude logic
func GetWildcardResult(expandedIncludes []string, expandedExcludes []string) []string {
	// Set check: set of expandedIncludes - set of expandedExcludes
	expandedIncludesSet := sets.New(expandedIncludes...)
	expandedExcludesSet := sets.New(expandedExcludes...)
	selectedNamespacesSet := expandedIncludesSet.Difference(expandedExcludesSet)

	// Convert the set to a slice
	selectedNamespaces := make([]string, 0, selectedNamespacesSet.Len())
	for ns := range selectedNamespacesSet {
		selectedNamespaces = append(selectedNamespaces, ns)
	}

	return selectedNamespaces
}
