package mimeutil

import (
	"sync"
	"testing"
)

// TestDetectContentType 测试 MIME 类型检测
func TestDetectContentType(t *testing.T) {
	tests := []struct {
		filePath string
		want     string
	}{
		// 标准库已知类型 (验证回退)
		{"test.html", "text/html; charset=utf-8"},
		{"test.css", "text/css; charset=utf-8"},
		{"test.js", "text/javascript; charset=utf-8"},
		{"test.json", "application/json"},
		{"test.mjs", "text/javascript; charset=utf-8"}, // Go 1.26.2+ 已支持

		// 包本地补充类型 - 缺失
		{"test.eot", "application/vnd.ms-fontobject"},
		{"test.webmanifest", "application/manifest+json"},
		{"test.map", "application/json"},

		// 包本地覆盖类型 - Go 返回错误类型
		{"test.otf", "font/otf"},    // Go 返回 OpenDocument 公式模板
		{"test.webm", "video/webm"}, // Go 返回 audio/webm

		// 大小写处理 - 验证 strings.ToLower
		{"test.EOT", "application/vnd.ms-fontobject"}, // 不在 Go 表中
		{"test.WEBMANIFEST", "application/manifest+json"},
		{"test.JPG", "image/jpeg"}, // Go 已知，也处理大小写

		// 未知类型
		{"test.unknown", ""},
	}

	for _, tt := range tests {
		t.Run(tt.filePath, func(t *testing.T) {
			got := DetectContentType(tt.filePath)
			if got != tt.want {
				t.Errorf("DetectContentType(%q) = %q, want %q", tt.filePath, got, tt.want)
			}
		})
	}
}

// TestAddTypes 测试自定义 MIME 类型添加
func TestAddTypes(t *testing.T) {
	tests := []struct {
		name     string
		types    map[string]string
		filePath string
		want     string
	}{
		{
			name:     "添加新扩展名",
			types:    map[string]string{".xyz": "application/x-xyz"},
			filePath: "test.xyz",
			want:     "application/x-xyz",
		},
		{
			name:     "覆盖已有映射",
			types:    map[string]string{".otf": "application/x-font-otf"},
			filePath: "test.otf",
			want:     "application/x-font-otf",
		},
		{
			name:     "大写扩展名自动转小写",
			types:    map[string]string{".ABC": "application/x-abc"},
			filePath: "test.abc",
			want:     "application/x-abc",
		},
		{
			name:     "多个映射同时添加",
			types:    map[string]string{".foo": "application/x-foo", ".bar": "application/x-bar"},
			filePath: "test.foo",
			want:     "application/x-foo",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// 保存原始值以便恢复
			original := make(map[string]string)
			for ext := range tt.types {
				original[ext] = mimeOverrides[ext]
			}

			AddTypes(tt.types)
			got := DetectContentType(tt.filePath)

			if got != tt.want {
				t.Errorf("AddTypes: DetectContentType(%q) = %q, want %q", tt.filePath, got, tt.want)
			}

			// 恢复原始值
			mimeMutex.Lock()
			for ext, orig := range original {
				if orig == "" {
					delete(mimeOverrides, ext)
				} else {
					mimeOverrides[ext] = orig
				}
			}
			mimeMutex.Unlock()
		})
	}
}

// TestSetDefaultType 测试设置默认 MIME 类型
func TestSetDefaultType(t *testing.T) {
	// 保存原始默认值
	original := GetDefaultType()

	tests := []struct {
		name string
		want string
	}{
		{"设置为 text/plain", "text/plain"},
		{"设置为 application/json", "application/json"},
		{"设置为空字符串", ""},
		{"恢复默认值", "application/octet-stream"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			SetDefaultType(tt.want)
			got := GetDefaultType()
			if got != tt.want {
				t.Errorf("SetDefaultType(%q): GetDefaultType() = %q, want %q", tt.want, got, tt.want)
			}
		})
	}

	// 恢复原始值
	SetDefaultType(original)
}

// TestGetDefaultType 测试获取默认 MIME 类型
func TestGetDefaultType(t *testing.T) {
	// 保存原始默认值
	original := GetDefaultType()

	// 验证默认值
	if original != "application/octet-stream" {
		t.Errorf("GetDefaultType() = %q, want %q", original, "application/octet-stream")
	}

	// 测试设置后获取
	SetDefaultType("text/plain")
	if got := GetDefaultType(); got != "text/plain" {
		t.Errorf("GetDefaultType() = %q, want %q", got, "text/plain")
	}

	// 恢复原始值
	SetDefaultType(original)
}

// TestConcurrentAccess 测试并发访问安全性
func TestConcurrentAccess(t *testing.T) {
	var wg sync.WaitGroup
	numGoroutines := 100

	// 并发调用 DetectContentType
	wg.Add(numGoroutines)
	for range numGoroutines {
		go func() {
			defer wg.Done()
			DetectContentType("test.html")
		}()
	}

	// 并发调用 AddTypes
	wg.Add(numGoroutines)
	for i := range numGoroutines {
		go func(i int) {
			defer wg.Done()
			AddTypes(map[string]string{".test": "application/x-test"})
		}(i)
	}

	// 并发调用 SetDefaultType 和 GetDefaultType
	wg.Add(numGoroutines)
	for range numGoroutines {
		go func() {
			defer wg.Done()
			SetDefaultType("text/plain")
			_ = GetDefaultType()
		}()
	}

	wg.Wait()

	// 恢复默认值
	SetDefaultType("application/octet-stream")
}
