// Package proxy 提供 HTTP 代理功能。
//
// 该文件实现 HealthMatch 健康检查匹配接口，支持：
//   - 默认 2xx 状态码判断
//   - 自定义状态码范围匹配
//   - 响应体正则匹配
//   - 响应头匹配
//
// 主要用途：
//
//	灵活定义后端服务器健康判断逻辑，替代硬编码的 2xx 判断。
//
// 作者：xfy
package proxy

import (
	"regexp"
	"strconv"
	"strings"
)

// HealthMatch 定义健康检查匹配接口。
//
// 用于判断健康检查响应是否表示目标健康。
type HealthMatch interface {
	// Match 判断健康检查响应是否表示目标健康。
	//
	// 参数：
	//   - status: HTTP 状态码
	//   - body: 响应体内容
	//   - headers: 响应头（key 为小写）
	//
	// 返回值：
	//   - true: 目标健康
	//   - false: 目标不健康
	Match(status int, body []byte, headers map[string]string) bool
}

// defaultHealthMatch 默认健康检查匹配器。
//
// 判断逻辑：状态码为 2xx 即健康。
type defaultHealthMatch struct{}

// Match 实现 HealthMatch 接口。
func (m *defaultHealthMatch) Match(status int, _ []byte, _ map[string]string) bool {
	return status >= 200 && status < 300
}

// customHealthMatch 自定义健康检查匹配器。
//
// 支持状态码范围、响应体正则、响应头匹配。
type customHealthMatch struct {
	statusRanges  []statusRange  // 状态码范围列表
	bodyRegex     *regexp.Regexp // 响应体正则（可选）
	headerMatches []headerMatch  // 响应头匹配列表（可选）
}

// statusRange 表示状态码范围。
type statusRange struct {
	min int
	max int
}

// headerMatch 表示响应头匹配条件。
type headerMatch struct {
	key   string
	value string
}

// Match 实现 HealthMatch 接口。
func (m *customHealthMatch) Match(status int, body []byte, headers map[string]string) bool {
	// 1. 检查状态码
	if !m.matchStatus(status) {
		return false
	}

	// 2. 检查响应体正则（如果配置）
	if m.bodyRegex != nil && !m.bodyRegex.Match(body) {
		return false
	}

	// 3. 检查响应头（如果配置）
	for _, hm := range m.headerMatches {
		value, exists := headers[hm.key]
		if !exists || value != hm.value {
			return false
		}
	}

	return true
}

// matchStatus 检查状态码是否匹配任一范围。
func (m *customHealthMatch) matchStatus(status int) bool {
	for _, r := range m.statusRanges {
		if status >= r.min && status <= r.max {
			return true
		}
	}
	return false
}

// HealthMatchConfig 健康检查匹配配置。
type HealthMatchConfig struct {
	// Status 状态码范围列表，如 ["200-299", "301"]
	Status []string `yaml:"status"`

	// Body 响应体正则表达式
	Body string `yaml:"body"`

	// Headers 响应头匹配，如 {"Content-Type": "application/json"}
	Headers map[string]string `yaml:"headers"`
}

// NewHealthMatch 从配置创建健康检查匹配器。
//
// 如果配置为空或无效，返回默认匹配器（2xx 判断）。
func NewHealthMatch(cfg *HealthMatchConfig) HealthMatch {
	if cfg == nil {
		return &defaultHealthMatch{}
	}

	// 解析状态码范围
	ranges := make([]statusRange, 0, len(cfg.Status))
	for _, s := range cfg.Status {
		r, err := parseStatusRange(s)
		if err != nil {
			continue // 忽略无效范围
		}
		ranges = append(ranges, r)
	}

	// 如果没有有效状态码范围，使用默认 2xx
	if len(ranges) == 0 {
		ranges = []statusRange{{min: 200, max: 299}}
	}

	// 解析响应体正则
	var bodyRegex *regexp.Regexp
	if cfg.Body != "" {
		bodyRegex = regexp.MustCompile(cfg.Body) // 配置加载时预编译
	}

	// 解析响应头匹配
	headerMatches := make([]headerMatch, 0, len(cfg.Headers))
	for k, v := range cfg.Headers {
		headerMatches = append(headerMatches, headerMatch{
			key:   strings.ToLower(k), // 统一小写
			value: v,
		})
	}

	return &customHealthMatch{
		statusRanges:  ranges,
		bodyRegex:     bodyRegex,
		headerMatches: headerMatches,
	}
}

// parseStatusRange 解析状态码范围字符串。
//
// 支持格式：
//   - "200" → 单个状态码
//   - "200-299" → 范围
func parseStatusRange(s string) (statusRange, error) {
	s = strings.TrimSpace(s)

	// 尝试解析范围
	if strings.Contains(s, "-") {
		parts := strings.Split(s, "-")
		if len(parts) != 2 {
			return statusRange{}, strconv.ErrSyntax
		}

		min, err1 := strconv.Atoi(strings.TrimSpace(parts[0]))
		max, err2 := strconv.Atoi(strings.TrimSpace(parts[1]))
		if err1 != nil || err2 != nil {
			return statusRange{}, strconv.ErrSyntax
		}

		return statusRange{min: min, max: max}, nil
	}

	// 单个状态码
	code, err := strconv.Atoi(s)
	if err != nil {
		return statusRange{}, err
	}

	return statusRange{min: code, max: code}, nil
}

// DefaultHealthMatch 返回默认健康检查匹配器。
func DefaultHealthMatch() HealthMatch {
	return &defaultHealthMatch{}
}
