// Package config 提供配置加载和管理的测试。
package config

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

// TestLoad 测试从文件加载配置。
func TestLoad(t *testing.T) {
	t.Run("有效配置文件", func(t *testing.T) {
		// 创建临时配置文件
		content := `
servers:
  - listen: ":8080"
    static:
      - path: "/"
        root: "/var/www"
        index:
          - "index.html"
logging:
  access:
    path: "/var/log/access.log"
    format: "combined"
  error:
    path: "/var/log/error.log"
    level: "info"
performance:
  goroutine_pool:
    enabled: true
    max_workers: 100
  file_cache:
    max_entries: 1000
monitoring:
  status:
    path: "/status"
`
		tmpDir := t.TempDir()
		tmpFile := filepath.Join(tmpDir, "config.yaml")
		if err := os.WriteFile(tmpFile, []byte(content), 0o644); err != nil {
			t.Fatalf("创建临时配置文件失败: %v", err)
		}

		cfg, err := Load(tmpFile)
		if err != nil {
			t.Fatalf("Load() 失败: %v", err)
		}

		if cfg.Servers[0].Listen != ":8080" {
			t.Errorf("Servers[0].Listen = %q, want %q", cfg.Servers[0].Listen, ":8080")
		}
		if cfg.Servers[0].Static[0].Root != "/var/www" {
			t.Errorf("Servers[0].Static.Root = %q, want %q", cfg.Servers[0].Static[0].Root, "/var/www")
		}
		if len(cfg.Servers[0].Static[0].Index) != 1 || cfg.Servers[0].Static[0].Index[0] != "index.html" {
			t.Errorf("Servers[0].Static.Index = %v, want [index.html]", cfg.Servers[0].Static[0].Index)
		}
	})

	t.Run("文件不存在", func(t *testing.T) {
		_, err := Load("/nonexistent/path/config.yaml")
		if err == nil {
			t.Error("Load() 期望返回错误，但返回 nil")
		}
	})

	t.Run("无效YAML", func(t *testing.T) {
		content := `
server:
  listen: ":8080"
  static:
    root: [invalid yaml structure
`
		tmpDir := t.TempDir()
		tmpFile := filepath.Join(tmpDir, "invalid.yaml")
		if err := os.WriteFile(tmpFile, []byte(content), 0o644); err != nil {
			t.Fatalf("创建临时配置文件失败: %v", err)
		}

		_, err := Load(tmpFile)
		if err == nil {
			t.Error("Load() 期望返回错误，但返回 nil")
		}
	})

	t.Run("缺少必填字段（无服务器配置）", func(t *testing.T) {
		content := `
logging:
  access:
    path: "/var/log/access.log"
`
		tmpDir := t.TempDir()
		tmpFile := filepath.Join(tmpDir, "no_server.yaml")
		if err := os.WriteFile(tmpFile, []byte(content), 0o644); err != nil {
			t.Fatalf("创建临时配置文件失败: %v", err)
		}

		_, err := Load(tmpFile)
		if err == nil {
			t.Error("Load() 期望返回错误，但返回 nil")
		}
	})

	t.Run("多虚拟主机模式", func(t *testing.T) {
		content := `
servers:
  - listen: ":8080"
    name: "server1"
  - listen: ":8081"
    name: "server2"
`
		tmpDir := t.TempDir()
		tmpFile := filepath.Join(tmpDir, "multi.yaml")
		if err := os.WriteFile(tmpFile, []byte(content), 0o644); err != nil {
			t.Fatalf("创建临时配置文件失败: %v", err)
		}

		cfg, err := Load(tmpFile)
		if err != nil {
			t.Fatalf("Load() 失败: %v", err)
		}

		if len(cfg.Servers) != 2 {
			t.Fatalf("len(Servers) = %d, want 2", len(cfg.Servers))
		}
		if cfg.Servers[0].Name != "server1" {
			t.Errorf("Servers[0].Name = %q, want %q", cfg.Servers[0].Name, "server1")
		}
		if cfg.Servers[1].Name != "server2" {
			t.Errorf("Servers[1].Name = %q, want %q", cfg.Servers[1].Name, "server2")
		}
	})
}

// TestConfigMethods 测试 Config 结构体的方法。

func TestProxyBufferingConfig_ParseBuffers(t *testing.T) {
	tests := []struct {
		name         string
		buffers      string
		bufferSize   int
		wantCount    int
		wantSizeEach int
	}{
		{
			name:         "empty uses buffer_size",
			buffers:      "",
			bufferSize:   4096,
			wantCount:    1,
			wantSizeEach: 4096,
		},
		{
			name:         "8 16k format",
			buffers:      "8 16k",
			wantCount:    8,
			wantSizeEach: 16 * 1024,
		},
		{
			name:         "4 4k format",
			buffers:      "4 4k",
			wantCount:    4,
			wantSizeEach: 4 * 1024,
		},
		{
			name:         "2 1m format",
			buffers:      "2 1m",
			wantCount:    2,
			wantSizeEach: 1024 * 1024,
		},
		{
			name:         "bytes without unit",
			buffers:      "4 8192",
			wantCount:    4,
			wantSizeEach: 8192,
		},
		{
			name:         "uppercase K",
			buffers:      "8 16K",
			wantCount:    8,
			wantSizeEach: 16 * 1024,
		},
		{
			name:         "invalid format",
			buffers:      "invalid",
			wantCount:    0,
			wantSizeEach: 0,
		},
		{
			name:         "missing size",
			buffers:      "8",
			wantCount:    0,
			wantSizeEach: 0,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cfg := &ProxyBufferingConfig{
				Buffers:    tt.buffers,
				BufferSize: tt.bufferSize,
			}
			cfg.ParseBuffers()

			if cfg.BufferCount != tt.wantCount {
				t.Errorf("BufferCount = %d, want %d", cfg.BufferCount, tt.wantCount)
			}
			if cfg.BufferSizeEach != tt.wantSizeEach {
				t.Errorf("BufferSizeEach = %d, want %d", cfg.BufferSizeEach, tt.wantSizeEach)
			}
		})
	}
}

func TestLoad_Include(t *testing.T) {
	t.Run("append servers from include", func(t *testing.T) {
		tmpDir := t.TempDir()

		mainCfg := `
servers:
  - listen: ":8080"
    name: main
include:
  - path: "conf.d/*.yaml"
`
		incCfg := `
servers:
  - listen: ":9090"
    name: included
`
		confDir := filepath.Join(tmpDir, "conf.d")
		if err := os.MkdirAll(confDir, 0o755); err != nil {
			t.Fatal(err)
		}
		if err := os.WriteFile(filepath.Join(tmpDir, "config.yaml"), []byte(mainCfg), 0o644); err != nil {
			t.Fatal(err)
		}
		if err := os.WriteFile(filepath.Join(confDir, "extra.yaml"), []byte(incCfg), 0o644); err != nil {
			t.Fatal(err)
		}

		cfg, err := Load(filepath.Join(tmpDir, "config.yaml"))
		if err != nil {
			t.Fatalf("Load() failed: %v", err)
		}

		if len(cfg.Servers) != 2 {
			t.Fatalf("expected 2 servers, got %d", len(cfg.Servers))
		}
		if cfg.Servers[0].Name != "main" {
			t.Errorf("Servers[0].Name = %q, want %q", cfg.Servers[0].Name, "main")
		}
		if cfg.Servers[1].Name != "included" {
			t.Errorf("Servers[1].Name = %q, want %q", cfg.Servers[1].Name, "included")
		}
	})

	t.Run("merge variables with main priority", func(t *testing.T) {
		tmpDir := t.TempDir()

		mainCfg := `
servers:
  - listen: ":8080"
variables:
  set:
    app: lolly
    env: production
include:
  - path: "extra.yaml"
`
		incCfg := `
servers:
  - listen: ":9090"
variables:
  set:
    app: other
    debug: "true"
`
		if err := os.WriteFile(filepath.Join(tmpDir, "config.yaml"), []byte(mainCfg), 0o644); err != nil {
			t.Fatal(err)
		}
		if err := os.WriteFile(filepath.Join(tmpDir, "extra.yaml"), []byte(incCfg), 0o644); err != nil {
			t.Fatal(err)
		}

		cfg, err := Load(filepath.Join(tmpDir, "config.yaml"))
		if err != nil {
			t.Fatalf("Load() failed: %v", err)
		}

		if cfg.Variables.Set["app"] != "lolly" {
			t.Errorf("app = %q, want %q (main should win)", cfg.Variables.Set["app"], "lolly")
		}
		if cfg.Variables.Set["debug"] != "true" {
			t.Errorf("debug = %q, want %q (included should fill missing)", cfg.Variables.Set["debug"], "true")
		}
		if cfg.Variables.Set["env"] != "production" {
			t.Errorf("env = %q, want %q", cfg.Variables.Set["env"], "production")
		}
	})

	t.Run("no matches returns error", func(t *testing.T) {
		tmpDir := t.TempDir()

		mainCfg := `
servers:
  - listen: ":8080"
include:
  - path: "nonexistent/*.yaml"
`
		if err := os.WriteFile(filepath.Join(tmpDir, "config.yaml"), []byte(mainCfg), 0o644); err != nil {
			t.Fatal(err)
		}

		_, err := Load(filepath.Join(tmpDir, "config.yaml"))
		if err == nil {
			t.Error("expected error for non-matching glob pattern")
		}
	})

	t.Run("circular include detected immediately", func(t *testing.T) {
		tmpDir := t.TempDir()

		cfg1 := `
servers:
  - listen: ":8080"
include:
  - path: "b.yaml"
`
		cfg2 := `
servers:
  - listen: ":9090"
include:
  - path: "a.yaml"
`
		if err := os.WriteFile(filepath.Join(tmpDir, "a.yaml"), []byte(cfg1), 0o644); err != nil {
			t.Fatal(err)
		}
		if err := os.WriteFile(filepath.Join(tmpDir, "b.yaml"), []byte(cfg2), 0o644); err != nil {
			t.Fatal(err)
		}

		_, err := Load(filepath.Join(tmpDir, "a.yaml"))
		if err == nil {
			t.Error("expected error for circular include")
		}
		if !strings.Contains(err.Error(), "循环引入") {
			t.Errorf("error should mention circular include, got: %v", err)
		}
	})

	t.Run("self include detected", func(t *testing.T) {
		tmpDir := t.TempDir()

		cfg := `
servers:
  - listen: ":8080"
include:
  - path: "a.yaml"
`
		if err := os.WriteFile(filepath.Join(tmpDir, "a.yaml"), []byte(cfg), 0o644); err != nil {
			t.Fatal(err)
		}

		_, err := Load(filepath.Join(tmpDir, "a.yaml"))
		if err == nil {
			t.Error("expected error for self include")
		}
	})

	t.Run("diamond include works", func(t *testing.T) {
		tmpDir := t.TempDir()

		mainCfg := `
servers:
  - listen: ":8080"
include:
  - path: "b.yaml"
  - path: "c.yaml"
`
		bCfg := `
servers:
  - listen: ":9090"
include:
  - path: "shared.yaml"
`
		cCfg := `
servers:
  - listen: ":9091"
include:
  - path: "shared.yaml"
`
		sharedCfg := `
variables:
  set:
    shared: value
`
		if err := os.WriteFile(filepath.Join(tmpDir, "config.yaml"), []byte(mainCfg), 0o644); err != nil {
			t.Fatal(err)
		}
		if err := os.WriteFile(filepath.Join(tmpDir, "b.yaml"), []byte(bCfg), 0o644); err != nil {
			t.Fatal(err)
		}
		if err := os.WriteFile(filepath.Join(tmpDir, "c.yaml"), []byte(cCfg), 0o644); err != nil {
			t.Fatal(err)
		}
		if err := os.WriteFile(filepath.Join(tmpDir, "shared.yaml"), []byte(sharedCfg), 0o644); err != nil {
			t.Fatal(err)
		}

		cfg, err := Load(filepath.Join(tmpDir, "config.yaml"))
		if err != nil {
			t.Fatalf("diamond include should work: %v", err)
		}

		if len(cfg.Servers) != 3 {
			t.Errorf("expected 3 servers, got %d", len(cfg.Servers))
		}
		if cfg.Variables.Set["shared"] != "value" {
			t.Error("shared variable should be merged")
		}
	})

	t.Run("empty include list is no-op", func(t *testing.T) {
		tmpDir := t.TempDir()

		mainCfg := `
servers:
  - listen: ":8080"
`
		if err := os.WriteFile(filepath.Join(tmpDir, "config.yaml"), []byte(mainCfg), 0o644); err != nil {
			t.Fatal(err)
		}

		cfg, err := Load(filepath.Join(tmpDir, "config.yaml"))
		if err != nil {
			t.Fatalf("Load() failed: %v", err)
		}
		if len(cfg.Servers) != 1 {
			t.Errorf("expected 1 server, got %d", len(cfg.Servers))
		}
	})

	t.Run("append stream from include", func(t *testing.T) {
		tmpDir := t.TempDir()

		mainCfg := `
servers:
  - listen: ":8080"
stream:
  - listen: ":5432"
    protocol: tcp
    upstream:
      targets:
        - addr: "127.0.0.1:9000"
include:
  - path: "stream.yaml"
`
		incCfg := `
stream:
  - listen: ":5433"
    protocol: udp
    upstream:
      targets:
        - addr: "127.0.0.1:9001"
`
		if err := os.WriteFile(filepath.Join(tmpDir, "config.yaml"), []byte(mainCfg), 0o644); err != nil {
			t.Fatal(err)
		}
		if err := os.WriteFile(filepath.Join(tmpDir, "stream.yaml"), []byte(incCfg), 0o644); err != nil {
			t.Fatal(err)
		}

		cfg, err := Load(filepath.Join(tmpDir, "config.yaml"))
		if err != nil {
			t.Fatalf("Load() failed: %v", err)
		}

		if len(cfg.Stream) != 2 {
			t.Fatalf("expected 2 streams, got %d", len(cfg.Stream))
		}
	})
}
