package wildcard

import (
	"regexp"
	"strings"
	"sync"

	"github.com/seaweedfs/seaweedfs/weed/glog"
)

// WildcardMatcher provides unified wildcard matching functionality.
type WildcardMatcher struct {
	useRegex bool
	regex    *regexp.Regexp
	pattern  string
}

// WildcardMatcherCache provides caching for WildcardMatcher instances.
type WildcardMatcherCache struct {
	mu          sync.RWMutex
	matchers    map[string]*WildcardMatcher
	maxSize     int
	accessOrder []string
}

// NewWildcardMatcherCache creates a new WildcardMatcherCache with a configurable maxSize.
func NewWildcardMatcherCache(maxSize int) *WildcardMatcherCache {
	if maxSize <= 0 {
		maxSize = 1000
	}
	return &WildcardMatcherCache{
		matchers: make(map[string]*WildcardMatcher),
		maxSize:  maxSize,
	}
}

var wildcardMatcherCache = NewWildcardMatcherCache(1000)

// GetCachedWildcardMatcher gets or creates a cached WildcardMatcher for the given pattern.
func GetCachedWildcardMatcher(pattern string) (*WildcardMatcher, error) {
	wildcardMatcherCache.mu.RLock()
	if matcher, exists := wildcardMatcherCache.matchers[pattern]; exists {
		wildcardMatcherCache.mu.RUnlock()
		wildcardMatcherCache.updateAccessOrder(pattern)
		return matcher, nil
	}
	wildcardMatcherCache.mu.RUnlock()

	wildcardMatcherCache.mu.Lock()
	defer wildcardMatcherCache.mu.Unlock()

	if matcher, exists := wildcardMatcherCache.matchers[pattern]; exists {
		wildcardMatcherCache.updateAccessOrderLocked(pattern)
		return matcher, nil
	}

	matcher, err := NewWildcardMatcher(pattern)
	if err != nil {
		return nil, err
	}

	if len(wildcardMatcherCache.matchers) >= wildcardMatcherCache.maxSize {
		wildcardMatcherCache.evictLeastRecentlyUsed()
	}

	wildcardMatcherCache.matchers[pattern] = matcher
	wildcardMatcherCache.accessOrder = append(wildcardMatcherCache.accessOrder, pattern)
	return matcher, nil
}

func (c *WildcardMatcherCache) updateAccessOrder(pattern string) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.updateAccessOrderLocked(pattern)
}

func (c *WildcardMatcherCache) updateAccessOrderLocked(pattern string) {
	for i, p := range c.accessOrder {
		if p == pattern {
			c.accessOrder = append(c.accessOrder[:i], c.accessOrder[i+1:]...)
			break
		}
	}
	c.accessOrder = append(c.accessOrder, pattern)
}

func (c *WildcardMatcherCache) evictLeastRecentlyUsed() {
	if len(c.accessOrder) == 0 {
		return
	}
	lruPattern := c.accessOrder[0]
	c.accessOrder = c.accessOrder[1:]
	delete(c.matchers, lruPattern)
}

func (c *WildcardMatcherCache) ClearCache() {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.matchers = make(map[string]*WildcardMatcher)
	c.accessOrder = c.accessOrder[:0]
}

func (c *WildcardMatcherCache) GetCacheStats() (size int, maxSize int) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return len(c.matchers), c.maxSize
}

// NewWildcardMatcher creates a new wildcard matcher for the given pattern.
func NewWildcardMatcher(pattern string) (*WildcardMatcher, error) {
	matcher := &WildcardMatcher{pattern: pattern, useRegex: false}
	return matcher, nil
}

// Match checks if a string matches the wildcard pattern.
func (m *WildcardMatcher) Match(str string) bool {
	if m.useRegex {
		return m.regex.MatchString(str)
	}
	return matchWildcardString(m.pattern, str)
}

// MatchesWildcard provides a simple function interface for wildcard matching.
func MatchesWildcard(pattern, str string) bool {
	return matchWildcardString(pattern, str)
}

// FastMatchesWildcard uses a cached WildcardMatcher for repeated pattern matching.
func FastMatchesWildcard(pattern, str string) bool {
	matcher, err := GetCachedWildcardMatcher(pattern)
	if err != nil {
		glog.Errorf("Error getting cached WildcardMatcher for pattern %s: %v. Falling back to MatchesWildcard.", pattern, err)
		return MatchesWildcard(pattern, str)
	}
	return matcher.Match(str)
}

// CompileWildcardPattern converts a wildcard pattern to a compiled regex.
func CompileWildcardPattern(pattern string) (*regexp.Regexp, error) {
	return compileWildcardPattern(pattern)
}

func matchWildcardString(pattern, str string) bool {
	if pattern == "*" {
		return true
	}
	if pattern == str {
		return true
	}

	targetIndex := 0
	patternIndex := 0
	lastStarIndex := -1
	lastStarMatchIndex := 0

	for targetIndex < len(str) {
		switch {
		case patternIndex < len(pattern) && (pattern[patternIndex] == '?' || pattern[patternIndex] == str[targetIndex]):
			targetIndex++
			patternIndex++
		case patternIndex < len(pattern) && pattern[patternIndex] == '*':
			lastStarIndex = patternIndex
			lastStarMatchIndex = targetIndex
			patternIndex++
		case lastStarIndex != -1:
			patternIndex = lastStarIndex + 1
			lastStarMatchIndex++
			targetIndex = lastStarMatchIndex
		default:
			return false
		}
	}

	for patternIndex < len(pattern) && pattern[patternIndex] == '*' {
		patternIndex++
	}
	return patternIndex == len(pattern)
}

func compileWildcardPattern(pattern string) (*regexp.Regexp, error) {
	escaped := regexp.QuoteMeta(pattern)
	escaped = strings.ReplaceAll(escaped, `\*`, `.*`)
	escaped = strings.ReplaceAll(escaped, `\?`, `.`)
	escaped = "^" + escaped + "$"
	return regexp.Compile(escaped)
}
