package route

import (
	"regexp"
	"strings"
)

// MatchResult encapsulates the result of a route match operation.
// By returning a result struct instead of modifying input parameters,
// we avoid hidden side effects and make the matching logic more testable.
type MatchResult struct {
	Matched    bool              // Whether the path matched
	PathParams map[string]string // Extracted path parameters (e.g., {id} → "123")
	Remaining  string            // Remaining path suffix after the matched prefix
}

// Matcher defines the interface for route matching strategies.
// This abstraction separates matching logic from routing logic,
// making it easier to add new matching strategies (e.g., glob patterns).
type Matcher interface {
	// Match attempts to match a path against this matcher's pattern.
	// Returns a MatchResult containing match status and extracted data.
	Match(path string) *MatchResult
}

// PrefixMatcher implements Matcher for static prefix matching.
// Use this for routes with fixed prefixes like "/pd" or "/tikv".
type PrefixMatcher struct {
	prefix string
}

// NewPrefixMatcher creates a matcher for static prefix matching.
func NewPrefixMatcher(prefix string) *PrefixMatcher {
	return &PrefixMatcher{prefix: prefix}
}

// Match checks if the path starts with the configured prefix at a path boundary.
// The prefix must end at a path segment boundary (end of path or followed by "/").
func (m *PrefixMatcher) Match(path string) *MatchResult {
	if !strings.HasPrefix(path, m.prefix) {
		return &MatchResult{Matched: false}
	}
	remaining := strings.TrimPrefix(path, m.prefix)
	// Ensure prefix ends at path boundary (not in the middle of a segment)
	if remaining != "" && !strings.HasPrefix(remaining, "/") {
		return &MatchResult{Matched: false}
	}
	return &MatchResult{
		Matched:   true,
		Remaining: remaining,
	}
}

// RegexMatcher implements Matcher for regex-based matching with path parameters.
// Use this for routes with dynamic segments like "/tibr/{tibr_id}".
type RegexMatcher struct {
	pattern    *regexp.Regexp
	paramNames []string
}

// NewRegexMatcher creates a matcher from a compiled regex and parameter names.
// The pattern should have capturing groups that correspond to paramNames.
// The last capturing group (if present) is treated as the remaining path.
func NewRegexMatcher(pattern *regexp.Regexp, paramNames []string) *RegexMatcher {
	return &RegexMatcher{
		pattern:    pattern,
		paramNames: paramNames,
	}
}

// Match attempts to match the path using the regex pattern.
// On success, extracts path parameters and remaining path suffix.
func (m *RegexMatcher) Match(path string) *MatchResult {
	matches := m.pattern.FindStringSubmatch(path)
	if matches == nil {
		return &MatchResult{Matched: false}
	}

	result := &MatchResult{
		Matched: true,
	}

	// Extract path parameters from capturing groups
	if len(m.paramNames) > 0 {
		result.PathParams = make(map[string]string, len(m.paramNames))
		for i, name := range m.paramNames {
			if i+1 < len(matches) {
				result.PathParams[name] = matches[i+1]
			}
		}
	}

	// The last capturing group after params is the remaining path
	remainingIdx := len(m.paramNames) + 1
	if remainingIdx < len(matches) {
		result.Remaining = matches[remainingIdx]
	}

	return result
}
