// variable_integration_test.go - 变量系统集成测试
//
// 测试变量系统与日志、代理、重写的端到端集成
//
// 作者：xfy
package integration

import (
	"bytes"
	"strings"
	"testing"
	"time"

	"github.com/valyala/fasthttp"
	"rua.plus/lolly/internal/config"
	"rua.plus/lolly/internal/logging"
	"rua.plus/lolly/internal/middleware/rewrite"
	"rua.plus/lolly/internal/variable"
)

// TestVariableEndToEndInLogging 测试变量在访问日志中的端到端使用
func TestVariableEndToEndInLogging(t *testing.T) {
	// 创建内存日志输出
	var buf bytes.Buffer

	cfg := &config.LoggingConfig{
		Access: config.AccessLogConfig{
			// 使用完整的 nginx 风格格式
			Format: "$remote_addr - $remote_user [$time_local] \"$request_method $request_uri $scheme\" $status $body_bytes_sent \"$http_user_agent\"",
		},
	}

	logger := logging.New(cfg)

	// 模拟请求
	ctx := &fasthttp.RequestCtx{}
	ctx.Request.SetRequestURI("/api/users?page=1")
	ctx.Request.Header.SetMethod("GET")
	ctx.Request.Header.SetHost("example.com")
	ctx.Request.Header.Set("User-Agent", "TestAgent/1.0")

	// 记录访问日志
	logger.LogAccess(ctx, 200, 1024, 50*time.Millisecond)

	// 验证输出（这里主要验证不 panic）
	_ = buf.String()
	t.Log("Logging with variables completed successfully")
}

// TestVariableInProxyHeaders 测试代理请求头中的变量
func TestVariableInProxyHeaders(t *testing.T) {
	// 创建请求
	ctx := &fasthttp.RequestCtx{}
	ctx.Request.SetRequestURI("/api/test")
	ctx.Request.Header.SetMethod("POST")
	ctx.Request.Header.SetHost("api.example.com")
	ctx.Request.Header.Set("X-Custom-Header", "original")

	// 测试变量展开
	vc := variable.NewContext(ctx)
	defer variable.ReleaseContext(vc)

	// 模拟代理配置中的 header 设置
	tests := []struct {
		template string
		contains []string
	}{
		{"X-Forwarded-Host: $host", []string{"api.example.com"}},
		{"X-Real-IP: $remote_addr", []string{"0.0.0.0"}},
		{"X-Request-ID: $request_id", []string{"-"}}, // 未设置时为 -
	}

	for _, tt := range tests {
		result := vc.Expand(tt.template)
		for _, expected := range tt.contains {
			if !strings.Contains(result, expected) {
				t.Errorf("Expand(%q) = %q, expected to contain %q", tt.template, result, expected)
			}
		}
	}
}

// TestVariableInRewriteRules 测试重写规则中的变量
func TestVariableInRewriteRules(t *testing.T) {
	// 创建带有变量的重写规则
	rules := []config.RewriteRule{
		{
			Pattern:     "^/api/(.*)$",
			Replacement: "/v1/$1",
			Flag:        "break",
		},
		{
			Pattern:     "^/redirect/(.*)$",
			Replacement: "/new/$1",
			Flag:        "redirect",
		},
	}

	mw, err := rewrite.New(rules)
	if err != nil {
		t.Fatalf("failed to create rewrite middleware: %v", err)
	}

	// 测试第一个规则
	t.Run("rewrite_with_capture", func(t *testing.T) {
		ctx := &fasthttp.RequestCtx{}
		ctx.Request.SetRequestURI("/api/users")
		ctx.Request.Header.SetMethod("GET")

		var capturedPath string
		next := func(c *fasthttp.RequestCtx) {
			capturedPath = string(c.Path())
		}

		handler := mw.Process(next)
		handler(ctx)

		if capturedPath != "/v1/users" {
			t.Errorf("expected path '/v1/users', got %q", capturedPath)
		}
	})

	// 测试重定向规则
	t.Run("redirect_with_capture", func(t *testing.T) {
		ctx := &fasthttp.RequestCtx{}
		ctx.Request.SetRequestURI("/redirect/old-page")
		ctx.Request.Header.SetMethod("GET")

		handler := mw.Process(func(_ *fasthttp.RequestCtx) {
			t.Error("should not reach next handler for redirect")
		})
		handler(ctx)

		// 验证重定向状态码
		if ctx.Response.StatusCode() != 302 {
			t.Errorf("expected status 302, got %d", ctx.Response.StatusCode())
		}
	})
}

// TestVariableCompatibilityWithNginx 测试与 nginx 变量格式的兼容性
func TestVariableCompatibilityWithNginx(t *testing.T) {
	ctx := &fasthttp.RequestCtx{}
	ctx.Request.SetRequestURI("/test/path?foo=bar&baz=qux")
	ctx.Request.Header.SetMethod("POST")
	ctx.Request.Header.SetHost("example.com")
	ctx.Request.Header.Set("User-Agent", "TestAgent/1.0")
	ctx.Request.Header.Set("Referer", "http://referrer.com")

	// 设置响应信息
	variable.SetResponseInfoInContext(ctx, 201, 2048, 100000000) // 100ms

	vc := variable.NewContext(ctx)
	defer variable.ReleaseContext(vc)

	// 设置 HTTP 头变量（logging 中会自动设置这些）
	vc.Set("http_referer", "http://referrer.com")
	vc.Set("http_user_agent", "TestAgent/1.0")
	vc.Set("remote_user", "-")

	// 测试 nginx 风格的组合日志格式
	logFormat := "$remote_addr - $remote_user [$time_local] \"$request_method $request_uri $scheme\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\""
	result := vc.Expand(logFormat)

	// 验证结果包含期望的部分
	expectedParts := []string{
		"POST",                // method
		"/test/path",          // path
		"foo=bar",             // query string
		"http",                // scheme
		"201",                 // status
		"2048",                // body_bytes_sent
		"http://referrer.com", // referer
		"TestAgent/1.0",       // user agent
	}

	for _, part := range expectedParts {
		if !strings.Contains(result, part) {
			t.Errorf("log output missing %q: got %q", part, result)
		}
	}

	t.Logf("Generated log: %s", result)
}

// TestVariablePerformance 测试变量展开性能
func TestVariablePerformance(t *testing.T) {
	ctx := &fasthttp.RequestCtx{}
	ctx.Request.SetRequestURI("/api/v1/users/123?active=true")
	ctx.Request.Header.SetMethod("GET")
	ctx.Request.Header.SetHost("api.example.com")

	vc := variable.NewContext(ctx)
	defer variable.ReleaseContext(vc)

	// 常见日志格式模板
	template := "$remote_addr - $remote_user [$time_local] \"$request_method $request_uri $scheme\" $status $body_bytes_sent \"$http_user_agent\""

	// 执行多次展开
	start := time.Now()
	iterations := 10000
	for range iterations {
		_ = vc.Expand(template)
	}
	elapsed := time.Since(start)

	// 计算平均时间
	avg := elapsed / time.Duration(iterations)
	t.Logf("Average expansion time: %v (iterations: %d)", avg, iterations)

	// 验证性能在合理范围内。
	// race 检测会显著放大执行时间，因此阈值放宽到 50μs。
	threshold := 50 * time.Microsecond
	if avg > threshold {
		t.Errorf("average time %v exceeds %v", avg, threshold)
	}
}

// TestVariableEdgeCases 测试变量的边界情况
func TestVariableEdgeCases(t *testing.T) {
	tests := []struct {
		name     string
		setup    func(*fasthttp.RequestCtx)
		template string
		expected string
	}{
		{
			name: "empty_template",
			setup: func(ctx *fasthttp.RequestCtx) {
				ctx.Request.SetRequestURI("/test")
			},
			template: "",
			expected: "",
		},
		{
			name: "no_variables",
			setup: func(ctx *fasthttp.RequestCtx) {
				ctx.Request.SetRequestURI("/test")
			},
			template: "static text without variables",
			expected: "static text without variables",
		},
		{
			name: "undefined_variable",
			setup: func(ctx *fasthttp.RequestCtx) {
				ctx.Request.SetRequestURI("/test")
			},
			template: "$undefined_var",
			expected: "$undefined_var", // 保持原样
		},
		{
			name: "mixed_defined_undefined",
			setup: func(ctx *fasthttp.RequestCtx) {
				ctx.Request.SetRequestURI("/test")
				ctx.Request.Header.SetHost("example.com")
			},
			template: "$host-$undefined",
			expected: "example.com-$undefined",
		},
		{
			name: "special_characters_in_value",
			setup: func(ctx *fasthttp.RequestCtx) {
				ctx.Request.SetRequestURI("/test%20path")
				ctx.Request.Header.SetHost("example.com")
			},
			template: "$uri",
			expected: "/test path", // fasthttp Path() 返回解码后的路径
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ctx := &fasthttp.RequestCtx{}
			tt.setup(ctx)

			vc := variable.NewContext(ctx)
			defer variable.ReleaseContext(vc)

			result := vc.Expand(tt.template)
			if result != tt.expected {
				t.Errorf("Expand(%q) = %q, want %q", tt.template, result, tt.expected)
			}
		})
	}
}

// TestVariableAllBuiltins 测试所有内置变量
func TestVariableAllBuiltins(t *testing.T) {
	ctx := &fasthttp.RequestCtx{}
	ctx.Request.SetRequestURI("/test/path?foo=bar")
	ctx.Request.Header.SetMethod("GET")
	ctx.Request.Header.SetHost("example.com")

	// 设置响应信息
	variable.SetResponseInfoInContext(ctx, 200, 1024, 50000000) // 50ms

	vc := variable.NewContext(ctx)
	defer variable.ReleaseContext(vc)

	// 测试所有内置变量
	builtinVars := []string{
		"host",
		"remote_addr",
		"remote_port",
		"request_uri",
		"uri",
		"args",
		"request_method",
		"scheme",
		"server_name",
		"server_port",
		"status",
		"body_bytes_sent",
		"request_time",
		"time_local",
		"time_iso8601",
		"request_id",
	}

	for _, varName := range builtinVars {
		t.Run(varName, func(t *testing.T) {
			value, ok := vc.Get(varName)
			if !ok {
				t.Errorf("builtin variable %q not found", varName)
				return
			}
			if value == "" && varName != "args" {
				t.Logf("Warning: %q is empty", varName)
			}
			t.Logf("%s = %q", varName, value)
		})
	}
}
