// Package rewrite 提供 URL 重写中间件，支持正则表达式匹配和多种重写标志。
//
// 该文件包含 URL 重写相关的核心功能，包括：
//   - 正则表达式匹配和替换
//   - 多种重写标志（last、redirect、permanent、break）
//   - 变量展开支持
//   - ReDoS 安全防护
//
// 主要用途：
//
//	用于实现类似 nginx rewrite 模块的 URL 重写功能，支持灵活的 URL 变换规则。
//
// 注意事项：
//   - 重写规则按配置顺序执行，FlagLast 规则会重新从第一条规则开始匹配
//   - 最大迭代次数限制防止无限循环
//   - 正则表达式会进行安全性检查，防止 ReDoS 攻击
//
// 作者：xfy
package rewrite

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/valyala/fasthttp"
	"rua.plus/lolly/internal/config"
	"rua.plus/lolly/internal/utils"
	"rua.plus/lolly/internal/variable"
)

// MaxRewriteIterations URL重写最大迭代次数，防止无限循环。
const MaxRewriteIterations = 10

// Flag 重写标志类型。
type Flag int

const (
	// FlagLast 继续匹配其他规则（nginx 行为：重新从第一条规则开始匹配）。
	// 匹配到规则后会重新从第一条规则开始遍历，用于多规则链式重写。
	FlagLast Flag = iota
	// FlagRedirect 返回 302 临时重定向。
	// 客户端收到 302 响应后重新请求新 URL，不会继续匹配后续规则。
	FlagRedirect
	// FlagPermanent 返回 301 永久重定向。
	// 客户端收到 301 响应后永久重定向到新 URL，不会继续匹配后续规则。
	FlagPermanent
	// FlagBreak 停止匹配规则。
	// 修改请求路径后终止重写流程，直接进入下一个处理器。
	FlagBreak
)

// parseFlag 解析配置中的标志字符串为 Flag 枚举值。
//
// 将配置字符串转换为对应的 Flag 类型，用于控制重写后的行为。
//
// 参数：
//   - s: 标志字符串，支持 "last"、"redirect"、"permanent"、"break"
//
// 返回值：
//   - Flag: 对应的标志枚举值，无法识别时返回 FlagLast
func parseFlag(s string) Flag {
	switch strings.ToLower(s) {
	case "redirect":
		return FlagRedirect
	case "permanent":
		return FlagPermanent
	case "break":
		return FlagBreak
	default:
		return FlagLast
	}
}

// Rule 编译后的重写规则。
type Rule struct {
	// pattern 正则匹配模式，用于匹配请求路径
	pattern *regexp.Regexp
	// replacement 替换字符串，支持 $1、$2 等捕获组和变量展开
	replacement string
	// flag 执行标志，控制重写后的行为（last/redirect/permanent/break）
	flag Flag
}

// Middleware URL 重写中间件。
type Middleware struct {
	// rules 编译后的规则列表，按配置顺序执行
	rules []Rule
}

// New 创建 URL 重写中间件。
//
// 编译配置中的重写规则，验证正则表达式安全性后返回中间件实例。
//
// 参数：
//   - rules: 重写规则配置列表，包含模式、替换和标志
//
// 返回值：
//   - *Middleware: 编译后的重写中间件
//   - error: 正则表达式无效或不安全时返回错误
func New(rules []config.RewriteRule) (*Middleware, error) {
	compiled := make([]Rule, 0, len(rules))
	for _, r := range rules {
		// 验证正则表达式安全性，防止 ReDoS
		if err := validateRegexSafety(r.Pattern); err != nil {
			return nil, fmt.Errorf("unsafe regex pattern %q: %w", r.Pattern, err)
		}

		re, err := regexp.Compile(r.Pattern)
		if err != nil {
			return nil, err
		}
		compiled = append(compiled, Rule{
			pattern:     re,
			replacement: r.Replacement,
			flag:        parseFlag(r.Flag),
		})
	}
	return &Middleware{rules: compiled}, nil
}

// validateRegexSafety 验证正则表达式的安全性，防止 ReDoS 攻击。
//
// 检测可能导致灾难性回溯的危险模式，如嵌套量词。
// 限制模式最大长度 1000 字符。
//
// 参数：
//   - pattern: 待验证的正则表达式字符串
//
// 返回值：
//   - error: 检测到不安全模式时返回错误
func validateRegexSafety(pattern string) error {
	// 限制模式长度
	if len(pattern) > 1000 {
		return fmt.Errorf("pattern too long (max 1000 chars)")
	}

	// 检测危险模式：嵌套量词
	// 例如：(\w+)+, (\d+)+, (a+)+, (.+)+
	dangerousPatterns := []string{
		`(\w+)+`, `(\d+)+`, `(a+)+`, `(.+)+`,
		`(\w*)*`, `(\d*)*`, `(a*)*`, `(.*)*`,
		`(\w+)?+`, `(\d+)?+`,
	}

	for _, dangerous := range dangerousPatterns {
		if strings.Contains(pattern, dangerous) {
			return fmt.Errorf("potential catastrophic backtracking pattern detected")
		}
	}

	return nil
}

// Name 返回中间件名称。
func (m *Middleware) Name() string {
	return "rewrite"
}

// Process 应用重写规则。
//
// 对请求路径执行正则匹配和替换，根据标志控制后续行为。
// 支持迭代重写（FlagLast 会重新从第一条规则开始匹配）。
//
// 参数：
//   - next: 下一个请求处理器
//
// 返回值：
//   - fasthttp.RequestHandler: 包装后的请求处理器
func (m *Middleware) Process(next fasthttp.RequestHandler) fasthttp.RequestHandler {
	return func(ctx *fasthttp.RequestCtx) {
		path := string(ctx.Path())
		originalPath := path

		// 全局迭代计数器，用于检测循环（每次重写都计入迭代）
		iterationCount := 0
		// 规则索引，支持FlagLast后重新开始匹配
		ruleIndex := 0

		for ruleIndex < len(m.rules) {
			// 步骤1: 检查迭代次数是否超过限制（防止无限循环）
			if iterationCount >= MaxRewriteIterations {
				utils.SendError(ctx, utils.ErrInternalError)
				return
			}

			rule := m.rules[ruleIndex]

			if rule.pattern.MatchString(path) {
				// 步骤2: 执行正则替换
				newPath := rule.pattern.ReplaceAllString(path, rule.replacement)

				// 步骤3: 对替换结果进行变量展开
				vc := variable.NewContext(ctx)
				newPath = vc.Expand(newPath)
				variable.ReleaseContext(vc)

				// 步骤4: 根据标志决定后续行为
				switch rule.flag {
				case FlagRedirect:
					// 302 临时重定向
					ctx.Redirect(newPath, fasthttp.StatusFound)
					return
				case FlagPermanent:
					// 301 永久重定向
					ctx.Redirect(newPath, fasthttp.StatusMovedPermanently)
					return
				case FlagBreak:
					// 修改路径后停止匹配，直接进入处理器
					ctx.Request.SetRequestURI(newPath)
					next(ctx)
					return
				case FlagLast:
					// 修改路径，并重新从第一条规则开始匹配（nginx兼容行为）
					path = newPath
					ctx.Request.SetRequestURI(path)
					iterationCount++ // 每次FlagLast重写都增加计数
					ruleIndex = 0    // 重新从第一条规则开始
					continue
				}
			}

			ruleIndex++
		}

		// 步骤5: 如果路径被修改过，需要重新设置
		if path != originalPath {
			ctx.Request.SetRequestURI(path)
		}

		next(ctx)
	}
}

// Rules 返回编译后的规则列表（用于调试）。
//
// 返回值：
//   - []Rule: 编译后的重写规则列表
func (m *Middleware) Rules() []Rule {
	return m.rules
}
