// Package matcher 提供 nginx 风格的 location 匹配引擎实现。
//
// 该文件实现统一的 LocationEngine，整合所有匹配策略，
// 按照 nginx 优先级顺序执行匹配。
//
// 匹配优先级从高到低：
//  1. 精确匹配（=）
//  2. 前缀优先匹配（^~）
//  3. 正则匹配（~, ~*）
//  4. 普通前缀匹配
//
// 作者：xfy
package matcher

import (
	"errors"
	"fmt"

	"github.com/valyala/fasthttp"
)

// LocationEngine 统一匹配引擎。
//
// 整合所有 location 匹配策略，按照 nginx 优先级顺序执行：
//   - 精确匹配：O(1) hash map 查找
//   - 前缀优先：Radix Tree 最长前缀匹配
//   - 正则匹配：按注册顺序逐个尝试
//   - 普通前缀：Radix Tree 最长前缀匹配
//
// 注意事项：
//   - 调用 MarkInitialized 后不可再添加 location
//   - 正则匹配器按注册顺序执行，配置时应将高频模式放在前面
type LocationEngine struct {
	// prefixPriorityTree ^~ 类型前缀优先匹配树（优先级 2）
	prefixPriorityTree *RadixTree

	// prefixTree 普通前缀匹配树（优先级 4）
	prefixTree *RadixTree

	// exactMatchers 精确匹配映射
	exactMatchers map[string]*ExactMatcher

	// namedMatchers 命名 location 映射
	namedMatchers map[string]*NamedMatcher

	// registeredPaths 已注册路径（用于冲突检测）
	registeredPaths map[string]string

	// regexMatchers 正则匹配器列表（按注册顺序）
	regexMatchers []*RegexMatcher

	// initialized 是否已完成初始化
	initialized bool
}

// NewLocationEngine 创建新的匹配引擎实例。
//
// 返回值：
//   - *LocationEngine: 初始化的引擎实例
func NewLocationEngine() *LocationEngine {
	return &LocationEngine{
		exactMatchers:      make(map[string]*ExactMatcher),
		prefixPriorityTree: NewRadixTree(),
		prefixTree:         NewRadixTree(),
		regexMatchers:      []*RegexMatcher{},
		namedMatchers:      make(map[string]*NamedMatcher),
		registeredPaths:    make(map[string]string),
	}
}

// AddExact 添加精确匹配 location。
//
// 参数：
//   - path: 精确匹配路径
//   - handler: 请求处理器
//   - internal: 是否为 internal location
//
// 返回值：
//   - error: 引擎已初始化或路径冲突时返回错误
func (e *LocationEngine) AddExact(path string, handler fasthttp.RequestHandler, internal bool) error {
	if e.initialized {
		return errors.New("LocationEngine already initialized")
	}

	if err := e.checkConflict(path, "exact"); err != nil {
		return err
	}

	matcher := NewExactMatcher(path, handler, 1, internal)
	e.exactMatchers[path] = matcher
	return nil
}

// AddPrefixPriority 添加 ^~ 前缀优先匹配 location。
//
// 参数：
//   - path: 前缀优先路径
//   - handler: 请求处理器
//   - internal: 是否为 internal location
//
// 返回值：
//   - error: 引擎已初始化或路径冲突时返回错误
func (e *LocationEngine) AddPrefixPriority(path string, handler fasthttp.RequestHandler, internal bool) error {
	if e.initialized {
		return errors.New("LocationEngine already initialized")
	}

	if err := e.checkConflict(path, "prefix_priority"); err != nil {
		return err
	}

	return e.prefixPriorityTree.Insert(path, handler, 2, "prefix_priority", internal)
}

// AddRegex 添加正则匹配 location。
//
// 参数：
//   - pattern: 正则表达式模式
//   - handler: 请求处理器
//   - caseInsensitive: 是否大小写不敏感（~* 模式）
//   - internal: 是否为 internal location
//
// 返回值：
//   - error: 引擎已初始化或正则表达式无效时返回错误
func (e *LocationEngine) AddRegex(pattern string, handler fasthttp.RequestHandler, caseInsensitive bool, internal bool) error {
	if e.initialized {
		return errors.New("LocationEngine already initialized")
	}

	matcher, err := NewRegexMatcher(pattern, handler, 3, caseInsensitive, internal)
	if err != nil {
		return fmt.Errorf("invalid regex pattern: %w", err)
	}

	e.regexMatchers = append(e.regexMatchers, matcher)
	return nil
}

// AddPrefix 添加普通前缀匹配 location。
//
// 参数：
//   - path: 前缀路径
//   - handler: 请求处理器
//   - internal: 是否为 internal location
//
// 返回值：
//   - error: 引擎已初始化或路径冲突时返回错误
func (e *LocationEngine) AddPrefix(path string, handler fasthttp.RequestHandler, internal bool) error {
	if e.initialized {
		return errors.New("LocationEngine already initialized")
	}

	if err := e.checkConflict(path, "prefix"); err != nil {
		return err
	}

	return e.prefixTree.Insert(path, handler, 4, "prefix", internal)
}

// AddNamed 添加命名 location。
//
// 命名 location 用于内部跳转（如 error_page），不直接匹配请求路径。
//
// 参数：
//   - name: location 名称（不含 @ 前缀）
//   - handler: 请求处理器
//
// 返回值：
//   - error: 引擎已初始化或名称重复时返回错误
func (e *LocationEngine) AddNamed(name string, handler fasthttp.RequestHandler) error {
	if e.initialized {
		return errors.New("LocationEngine already initialized")
	}

	if _, ok := e.namedMatchers[name]; ok {
		return &ConflictError{
			Path:         "@" + name,
			ExistingType: "named",
			NewType:      "named",
		}
	}

	matcher := NewNamedMatcher(name, handler)
	e.namedMatchers[name] = matcher
	return nil
}

// Match 统一匹配入口。
//
// 按照 nginx 优先级顺序执行匹配：
//  1. 精确匹配（=）- O(1)
//  2. 前缀优先匹配（^~）- O(log n)
//  3. 正则匹配（~, ~*）- 按顺序
//  4. 普通前缀匹配 - O(log n)
//
// 参数：
//   - path: 请求路径
//
// 返回值：
//   - *MatchResult: 匹配结果，无匹配时返回 nil
func (e *LocationEngine) Match(path []byte) *MatchResult {
	if m, ok := e.exactMatchers[string(path)]; ok {
		return m.Result()
	}

	prefixPriorityResult := e.prefixPriorityTree.FindLongestPrefix(path)
	if prefixPriorityResult != nil && prefixPriorityResult.Handler != nil {
		return prefixPriorityResult
	}
	ReleaseMatchResult(prefixPriorityResult)

	pathStr := string(path)
	for _, m := range e.regexMatchers {
		if m.Match(pathStr) {
			result := m.Result()
			result.Captures = m.GetCaptures(pathStr)
			return result
		}
	}

	return e.prefixTree.FindLongestPrefix(path)
}

// MarkInitialized 标记初始化完成。
//
// 调用后所有 Add 方法均返回错误，确保运行时安全。
func (e *LocationEngine) MarkInitialized() {
	e.initialized = true
	e.prefixPriorityTree.MarkInitialized()
	e.prefixTree.MarkInitialized()
}

// ConflictError 路径冲突错误。
//
// 当同一路径被重复注册为不同类型的 location 时返回此错误。
// 调用方可通过 errors.As 检测此类型，区分冲突与致命错误。
type ConflictError struct {
	Path         string
	ExistingType string
	NewType      string
}

// Error 返回路径冲突错误的字符串表示。
func (e *ConflictError) Error() string {
	return fmt.Sprintf("path conflict: '%s' already registered as '%s', trying to register as '%s'",
		e.Path, e.ExistingType, e.NewType)
}

// checkConflict 检查路径冲突。
//
// 参数：
//   - path: 待注册的路径
//   - locationType: location 类型
//
// 返回值：
//   - error: 路径已存在时返回 *ConflictError
func (e *LocationEngine) checkConflict(path, locationType string) error {
	if existing, ok := e.registeredPaths[path]; ok {
		return &ConflictError{Path: path, ExistingType: existing, NewType: locationType}
	}
	e.registeredPaths[path] = locationType
	return nil
}
