// Package compression 提供 gzip_static 预压缩文件功能的测试。
//
// 该文件测试 gzip_static 模块的各项功能，包括：
//   - Brotli 和 Gzip 文件优先级
//   - Gzip 回退机制
//   - Accept-Encoding 头解析
//   - 扩展名检查
//   - 路径遍历防护
//   - Vary 头设置
//
// 作者：xfy
package compression

import (
	"os"
	"path/filepath"
	"testing"

	"github.com/valyala/fasthttp"
)

// TestGzipStaticServeFile_BrotliPriority 测试 .br 文件优先于 .gz 文件
func TestGzipStaticServeFile_BrotliPriority(t *testing.T) {
	// 创建临时目录
	tmpDir := t.TempDir()

	// 创建 .br 和 .gz 文件
	brFile := filepath.Join(tmpDir, "test.js.br")
	gzFile := filepath.Join(tmpDir, "test.js.gz")

	if err := os.WriteFile(brFile, []byte("br content"), 0o644); err != nil {
		t.Fatalf("创建 .br 文件失败: %v", err)
	}
	if err := os.WriteFile(gzFile, []byte("gz content"), 0o644); err != nil {
		t.Fatalf("创建 .gz 文件失败: %v", err)
	}

	g := NewGzipStatic(true, tmpDir, nil, nil)

	tests := []struct {
		acceptEncoding string
		name           string
		wantEncoding   string
		wantServed     bool
	}{
		{
			name:           "同时支持 br 和 gzip，优先返回 br",
			acceptEncoding: "br, gzip",
			wantServed:     true,
			wantEncoding:   "br",
		},
		{
			name:           "只支持 br",
			acceptEncoding: "br",
			wantServed:     true,
			wantEncoding:   "br",
		},
		{
			name:           "只支持 gzip",
			acceptEncoding: "gzip",
			wantServed:     true,
			wantEncoding:   "gzip",
		},
		{
			name:           "支持 deflate 和 gzip，返回 gzip",
			acceptEncoding: "deflate, gzip",
			wantServed:     true,
			wantEncoding:   "gzip",
		},
		{
			name:           "不支持任何编码",
			acceptEncoding: "",
			wantServed:     false,
			wantEncoding:   "",
		},
		{
			name:           "支持 br（大小写不敏感）",
			acceptEncoding: "BR",
			wantServed:     true,
			wantEncoding:   "br",
		},
		{
			name:           "支持 gzip（大小写不敏感）",
			acceptEncoding: "GZIP",
			wantServed:     true,
			wantEncoding:   "gzip",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ctx := &fasthttp.RequestCtx{}
			ctx.Request.Header.Set("Accept-Encoding", tt.acceptEncoding)

			served := g.ServeFile(ctx, "test.js")

			if served != tt.wantServed {
				t.Errorf("ServeFile() = %v, want %v", served, tt.wantServed)
			}

			if tt.wantServed {
				encoding := ctx.Response.Header.Peek("Content-Encoding")
				if string(encoding) != tt.wantEncoding {
					t.Errorf("Content-Encoding = %q, want %q", string(encoding), tt.wantEncoding)
				}
			}
		})
	}
}

// TestGzipStaticServeFile_GzipFallback 测试仅 .gz 存在时的回退
func TestGzipStaticServeFile_GzipFallback(t *testing.T) {
	// 创建临时目录
	tmpDir := t.TempDir()

	// 只创建 .gz 文件
	gzFile := filepath.Join(tmpDir, "test.css.gz")
	if err := os.WriteFile(gzFile, []byte("gz content"), 0o644); err != nil {
		t.Fatalf("创建 .gz 文件失败: %v", err)
	}

	g := NewGzipStatic(true, tmpDir, nil, nil)

	tests := []struct {
		acceptEncoding string
		name           string
		wantEncoding   string
		wantServed     bool
	}{
		{
			name:           "支持 br 但没有 .br 文件，回退到 gzip",
			acceptEncoding: "br, gzip",
			wantServed:     true,
			wantEncoding:   "gzip",
		},
		{
			name:           "只支持 gzip",
			acceptEncoding: "gzip",
			wantServed:     true,
			wantEncoding:   "gzip",
		},
		{
			name:           "只支持 br，没有 .br 文件，不服务",
			acceptEncoding: "br",
			wantServed:     false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ctx := &fasthttp.RequestCtx{}
			ctx.Request.Header.Set("Accept-Encoding", tt.acceptEncoding)

			served := g.ServeFile(ctx, "test.css")

			if served != tt.wantServed {
				t.Errorf("ServeFile() = %v, want %v", served, tt.wantServed)
			}

			if tt.wantServed {
				encoding := ctx.Response.Header.Peek("Content-Encoding")
				if string(encoding) != tt.wantEncoding {
					t.Errorf("Content-Encoding = %q, want %q", string(encoding), tt.wantEncoding)
				}
			}
		})
	}
}

// TestGzipStaticServeFile_AcceptEncodingParsing 测试 Accept-Encoding 头解析
func TestGzipStaticServeFile_AcceptEncodingParsing(t *testing.T) {
	tmpDir := t.TempDir()

	// 创建测试文件
	brFile := filepath.Join(tmpDir, "test.html.br")
	if err := os.WriteFile(brFile, []byte("br content"), 0o644); err != nil {
		t.Fatalf("创建 .br 文件失败: %v", err)
	}

	g := NewGzipStatic(true, tmpDir, nil, nil)

	tests := []struct {
		name           string
		acceptEncoding string
		wantSupported  bool
	}{
		{
			name:           "包含 br",
			acceptEncoding: "gzip, deflate, br",
			wantSupported:  true,
		},
		{
			name:           "br 在中间",
			acceptEncoding: "gzip, br, deflate",
			wantSupported:  true,
		},
		{
			name:           "br 在最后",
			acceptEncoding: "gzip, deflate,br",
			wantSupported:  true,
		},
		{
			name:           "包含空格",
			acceptEncoding: "gzip, br , deflate",
			wantSupported:  true,
		},
		{
			name:           "q-value 支持",
			acceptEncoding: "br;q=0.9, gzip;q=0.8",
			wantSupported:  true,
		},
		{
			name:           "没有 br",
			acceptEncoding: "gzip, deflate",
			wantSupported:  false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ctx := &fasthttp.RequestCtx{}
			ctx.Request.Header.Set("Accept-Encoding", tt.acceptEncoding)

			served := g.ServeFile(ctx, "test.html")

			if served != tt.wantSupported {
				t.Errorf("ServeFile() = %v, want %v", served, tt.wantSupported)
			}
		})
	}
}

// TestGzipStaticServeFile_Disabled 测试禁用时不服务
func TestGzipStaticServeFile_Disabled(t *testing.T) {
	tmpDir := t.TempDir()

	// 创建测试文件
	brFile := filepath.Join(tmpDir, "test.js.br")
	if err := os.WriteFile(brFile, []byte("br content"), 0o644); err != nil {
		t.Fatalf("创建 .br 文件失败: %v", err)
	}

	// 禁用的 GzipStatic
	g := NewGzipStatic(false, tmpDir, nil, nil)

	ctx := &fasthttp.RequestCtx{}
	ctx.Request.Header.Set("Accept-Encoding", "br")

	served := g.ServeFile(ctx, "test.js")

	if served {
		t.Error("禁用时不应服务文件")
	}
}

// TestGzipStaticServeFile_InvalidExtension 测试无效扩展名
func TestGzipStaticServeFile_InvalidExtension(t *testing.T) {
	tmpDir := t.TempDir()

	// 创建测试文件
	brFile := filepath.Join(tmpDir, "test.exe.br")
	if err := os.WriteFile(brFile, []byte("br content"), 0o644); err != nil {
		t.Fatalf("创建 .br 文件失败: %v", err)
	}

	g := NewGzipStatic(true, tmpDir, nil, nil)

	ctx := &fasthttp.RequestCtx{}
	ctx.Request.Header.Set("Accept-Encoding", "br")

	served := g.ServeFile(ctx, "test.exe")

	if served {
		t.Error("无效扩展名不应服务文件")
	}
}

// TestGzipStaticServeFile_PathTraversal 测试路径遍历防护
func TestGzipStaticServeFile_PathTraversal(t *testing.T) {
	tmpDir := t.TempDir()

	// 创建测试文件
	brFile := filepath.Join(tmpDir, "test.js.br")
	if err := os.WriteFile(brFile, []byte("br content"), 0o644); err != nil {
		t.Fatalf("创建 .br 文件失败: %v", err)
	}

	g := NewGzipStatic(true, tmpDir, nil, nil)

	ctx := &fasthttp.RequestCtx{}
	ctx.Request.Header.Set("Accept-Encoding", "br")

	// 尝试路径遍历
	served := g.ServeFile(ctx, "../test.js")

	if served {
		t.Error("路径遍历应被阻止")
	}
}

// TestGzipStaticServeFile_VaryHeader 测试 Vary 头设置
func TestGzipStaticServeFile_VaryHeader(t *testing.T) {
	tmpDir := t.TempDir()

	// 创建测试文件
	brFile := filepath.Join(tmpDir, "test.js.br")
	if err := os.WriteFile(brFile, []byte("br content"), 0o644); err != nil {
		t.Fatalf("创建 .br 文件失败: %v", err)
	}

	g := NewGzipStatic(true, tmpDir, nil, nil)

	ctx := &fasthttp.RequestCtx{}
	ctx.Request.Header.Set("Accept-Encoding", "br")

	g.ServeFile(ctx, "test.js")

	vary := ctx.Response.Header.Peek("Vary")
	if string(vary) != "Accept-Encoding" {
		t.Errorf("Vary 头 = %q, want %q", string(vary), "Accept-Encoding")
	}
}

// TestSupportsEncoding 测试 supportsEncoding 函数
func TestSupportsEncoding(t *testing.T) {
	tests := []struct {
		name           string
		acceptEncoding string
		wantEncoding   string
		wantSupported  bool
	}{
		{
			name:           "支持 br",
			acceptEncoding: "br",
			wantEncoding:   ".br",
			wantSupported:  true,
		},
		{
			name:           "支持 gzip",
			acceptEncoding: "gzip",
			wantEncoding:   ".gz",
			wantSupported:  true,
		},
		{
			name:           "不支持",
			acceptEncoding: "deflate",
			wantEncoding:   ".br",
			wantSupported:  false,
		},
		{
			name:           "空 Accept-Encoding",
			acceptEncoding: "",
			wantEncoding:   ".br",
			wantSupported:  false,
		},
		{
			name:           "br 在中间",
			acceptEncoding: "gzip, br, deflate",
			wantEncoding:   ".br",
			wantSupported:  true,
		},
		{
			name:           "gzip 在中间",
			acceptEncoding: "br, gzip, deflate",
			wantEncoding:   ".gz",
			wantSupported:  true,
		},
		{
			name:           "大小写不敏感",
			acceptEncoding: "BR, GZIP",
			wantEncoding:   ".br",
			wantSupported:  true,
		},
		{
			name:           "未知扩展名",
			acceptEncoding: "br, gzip",
			wantEncoding:   ".unknown",
			wantSupported:  false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			supported := supportsEncoding([]byte(tt.acceptEncoding), tt.wantEncoding)
			if supported != tt.wantSupported {
				t.Errorf("supportsEncoding(%q, %q) = %v, want %v",
					tt.acceptEncoding, tt.wantEncoding, supported, tt.wantSupported)
			}
		})
	}
}

// TestTryServeFile 测试预压缩文件服务（通过 ServeFile 间接测试）
func TestTryServeFile(t *testing.T) {
	tmpDir := t.TempDir()

	// 创建测试文件
	brFile := filepath.Join(tmpDir, "test.js.br")
	if err := os.WriteFile(brFile, []byte("br content"), 0o644); err != nil {
		t.Fatalf("创建 .br 文件失败: %v", err)
	}

	ctx := &fasthttp.RequestCtx{}
	ctx.Request.Header.Set("Accept-Encoding", "br")

	g := NewGzipStatic(true, tmpDir, nil, nil)
	served := g.ServeFile(ctx, "test.js")

	if !served {
		t.Error("ServeFile() = false, want true")
	}

	encoding := ctx.Response.Header.Peek("Content-Encoding")
	if string(encoding) != "br" {
		t.Errorf("Content-Encoding = %q, want %q", string(encoding), "br")
	}
}

// TestGzipStatic_PrecompressedExtensions 测试预压缩扩展名优先级
func TestGzipStatic_PrecompressedExtensions(t *testing.T) {
	g := NewGzipStatic(true, "/tmp", nil, nil)

	// 验证默认预压缩扩展名顺序
	expected := []string{".br", ".gz"}
	if len(g.precompressedExtensions) != len(expected) {
		t.Errorf("预压缩扩展名数量 = %d, want %d", len(g.precompressedExtensions), len(expected))
	}

	for i, ext := range expected {
		if g.precompressedExtensions[i] != ext {
			t.Errorf("预压缩扩展名[%d] = %q, want %q", i, g.precompressedExtensions[i], ext)
		}
	}
}
