// Copyright (c) 2024-2026 Tencent Zhuque Lab. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Requirement: Any integration or derivative work must explicitly attribute
// Tencent Zhuque Lab (https://github.com/Tencent/AI-Infra-Guard) in its
// documentation or user interface, as detailed in the NOTICE file.

package websocket

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"

	"trpc.group/trpc-go/trpc-go/log"

	"github.com/Tencent/AI-Infra-Guard/common/fingerprints/parser"
	"github.com/Tencent/AI-Infra-Guard/pkg/vulstruct"
	"github.com/gin-gonic/gin"
	"gopkg.in/yaml.v3"
)

// 合法性校验
var validName = regexp.MustCompile(`^[a-zA-Z0-9\\._ -]+$`)

func isValidName(name string) bool {
	if strings.Contains(name, "..") {
		return false
	}
	return validName.MatchString(name)
}

// safeJoinPath joins base and elem using filepath.Join, then verifies the result
// is still within base to prevent path traversal attacks (defense in depth).
func safeJoinPath(base, elem string) (string, error) {
	cleanBase := filepath.Clean(base)
	joined := filepath.Clean(filepath.Join(cleanBase, elem))
	if joined != cleanBase && !strings.HasPrefix(joined, cleanBase+string(os.PathSeparator)) {
		return "", fmt.Errorf("path traversal detected: %q is outside base %q", elem, base)
	}
	return joined, nil
}

// FingerprintWithTime 包含指纹数据和文件修改时间的结构体
type FingerprintWithTime struct {
	FingerPrint parser.FingerPrint
	ModTime     time.Time
}

// 评测集数据结构定义
type EvaluationDataItem struct {
	Prompt string `json:"prompt"`
}

type EvaluationDataset struct {
	Name           string               `json:"name"`
	Description    string               `json:"description"`
	DescriptionZh  string               `json:"description_zh,omitempty"`
	Author         string               `json:"author,omitempty"`
	Source         []string             `json:"source,omitempty"`
	Count          int                  `json:"count"`
	Default        bool                 `json:"default"`
	Tags           []string             `json:"tags,omitempty"`
	Recommendation int                  `json:"recommendation,omitempty"`
	Language       string               `json:"language,omitempty"`
	Data           []EvaluationDataItem `json:"data"`
}

// 获取指纹列表，支持分页和名字模糊
func HandleListFingerprints(c *gin.Context) {
	// 1. 解析分页参数
	pageStr := c.DefaultQuery("page", "1")
	sizeStr := c.DefaultQuery("size", "20")
	page, _ := strconv.Atoi(pageStr)
	size, _ := strconv.Atoi(sizeStr)
	if page < 1 {
		page = 1
	}
	if size < 1 {
		size = 10
	}

	// 2. 获取查询参数
	nameQuery := strings.ToLower(c.DefaultQuery("q", ""))

	// 3. 读取 data/fingerprints/ 下所有分类和YAML文件
	var allFingerprintsWithTime []FingerprintWithTime
	root := "data/fingerprints"
	filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
		if err != nil {
			return nil // 忽略错误
		}
		if !d.IsDir() && strings.HasSuffix(d.Name(), ".yaml") {
			content, _ := os.ReadFile(path)
			fp, err := parser.InitFingerPrintFromData(content)
			if err == nil && fp != nil {
				// 获取文件修改时间
				fileInfo, _ := d.Info()
				modTime := time.Time{}
				if fileInfo != nil {
					modTime = fileInfo.ModTime()
				}
				allFingerprintsWithTime = append(allFingerprintsWithTime, FingerprintWithTime{
					FingerPrint: *fp,
					ModTime:     modTime,
				})
			}
		}
		return nil
	})

	// 4. 条件过滤
	var filteredFingerprintsWithTime []FingerprintWithTime
	if nameQuery == "" {
		filteredFingerprintsWithTime = allFingerprintsWithTime
	} else {
		for _, fpWithTime := range allFingerprintsWithTime {
			fp := fpWithTime.FingerPrint
			if strings.Contains(strings.ToLower(fp.Info.Name), nameQuery) {
				filteredFingerprintsWithTime = append(filteredFingerprintsWithTime, fpWithTime)
				continue
			}
			if strings.Contains(strings.ToLower(fp.Info.Desc), nameQuery) {
				filteredFingerprintsWithTime = append(filteredFingerprintsWithTime, fpWithTime)
				continue
			}
			if strings.Contains(strings.ToLower(fp.Info.Author), nameQuery) {
				filteredFingerprintsWithTime = append(filteredFingerprintsWithTime, fpWithTime)
				continue
			}
		}
	}

	// 5. 复合排序：如果有Recommendation，先按Recommendation降序；否则按文件修改时间降序
	sort.Slice(filteredFingerprintsWithTime, func(i, j int) bool {
		fpI := filteredFingerprintsWithTime[i].FingerPrint
		fpJ := filteredFingerprintsWithTime[j].FingerPrint

		// 如果两个都有Recommendation（>0），按Recommendation降序
		if fpI.Info.Recommendation > 0 && fpJ.Info.Recommendation > 0 {
			if fpI.Info.Recommendation != fpJ.Info.Recommendation {
				return fpI.Info.Recommendation > fpJ.Info.Recommendation
			}
			// Recommendation相同时，按文件修改时间降序
			return filteredFingerprintsWithTime[i].ModTime.After(filteredFingerprintsWithTime[j].ModTime)
		}

		// 如果只有一个有Recommendation，有Recommendation的排前面
		if fpI.Info.Recommendation > 0 && fpJ.Info.Recommendation <= 0 {
			return true
		}
		if fpI.Info.Recommendation <= 0 && fpJ.Info.Recommendation > 0 {
			return false
		}

		// 如果两个都没有Recommendation，按文件修改时间降序
		return filteredFingerprintsWithTime[i].ModTime.After(filteredFingerprintsWithTime[j].ModTime)
	})

	// 提取指纹数据用于分页和返回
	var filteredFingerprints []parser.FingerPrint
	for _, fpWithTime := range filteredFingerprintsWithTime {
		filteredFingerprints = append(filteredFingerprints, fpWithTime.FingerPrint)
	}

	// 6. 分页
	total := len(filteredFingerprints)
	start := (page - 1) * size
	end := start + size
	if start > total {
		start = total
	}
	if end > total {
		end = total
	}
	items := filteredFingerprints[start:end]

	// 7. 返回
	c.JSON(http.StatusOK, gin.H{
		"status":  0,
		"message": "success",
		"data": gin.H{
			"total": total,
			"page":  page,
			"size":  size,
			"items": items,
		},
	})
}

// 创建指纹
func HandleCreateFingerprint(c *gin.Context) {
	// 1. 解析请求体，获取file_content字段
	type FingerprintUploadRequest struct {
		FileContent string `json:"file_content" binding:"required"`
	}
	var req FingerprintUploadRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "参数解析失败"})
		return
	}

	// 2. 解析YAML为parser.FingerPrint结构体
	var fp parser.FingerPrint
	if err := yaml.Unmarshal([]byte(req.FileContent), &fp); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "YAML解析失败: " + err.Error()})
		return
	}
	if fp.Info.Name == "" {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "指纹名称不能为空"})
		return
	}

	if _, err := parser.InitFingerPrintFromData([]byte(req.FileContent)); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "指纹内容校验失败: " + err.Error()})
		return
	}

	if !isValidName(fp.Info.Name) {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "指纹名称非法"})
		return
	}
	yamlPath, err := safeJoinPath("data/fingerprints", fp.Info.Name+".yaml")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "非法路径"})
		return
	}
	if _, err := os.Stat(yamlPath); err == nil {
		c.JSON(http.StatusConflict, gin.H{"status": 1, "message": "指纹已存在"})
		return
	}

	if err := os.WriteFile(yamlPath, []byte(req.FileContent), 0644); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"status": 1, "message": "文件写入失败: " + err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"status": 0, "message": "创建指纹成功"})
}

// 批量删除指纹处理函数
type BatchDeleteRequest struct {
	Name []string `json:"name"`
}

func HandleDeleteFingerprint(c *gin.Context) {
	var req BatchDeleteRequest
	if err := c.ShouldBindJSON(&req); err != nil || len(req.Name) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "参数错误", "data": nil})
		return
	}

	var deleted []string
	var notFound []string
	var invalid []string

	for _, name := range req.Name {
		// 使用已存在的合法性校验函数防止路径遍历攻击
		if !isValidName(name) {
			invalid = append(invalid, name)
			continue
		}
		yamlPath, pathErr := safeJoinPath("data/fingerprints", name+".yaml")
		if pathErr != nil {
			invalid = append(invalid, name)
			continue
		}
		if _, err := os.Stat(yamlPath); os.IsNotExist(err) {
			notFound = append(notFound, name)
			continue
		}
		if err := os.Remove(yamlPath); err == nil {
			deleted = append(deleted, name)
		}
	}

	msg := "删除完成"
	if len(notFound) > 0 {
		msg += "，部分指纹未找到: " + strings.Join(notFound, ", ")
	}
	if len(invalid) > 0 {
		msg += "，部分指纹名称非法: " + strings.Join(invalid, ", ")
	}

	c.JSON(http.StatusOK, gin.H{
		"status":  0,
		"message": msg,
		"data": gin.H{
			"deleted":  deleted,
			"notFound": notFound,
		},
	})
}

// 编辑指纹处理函数
func HandleEditFingerprint(c *gin.Context) {
	// 1. 获取原指纹名称
	oldName := c.Param("name")
	if oldName == "" {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "指纹名称不能为空"})
		return
	}

	type FingerprintUploadRequest struct {
		FileContent string `json:"file_content" binding:"required"`
	}
	var req FingerprintUploadRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "参数解析失败"})
		return
	}
	// 2. 解析YAML为parser.FingerPrint结构体
	var fp parser.FingerPrint
	if err := yaml.Unmarshal([]byte(req.FileContent), &fp); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "YAML解析失败: " + err.Error()})
		return
	}
	if fp.Info.Name == "" {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "指纹名称不能为空"})
		return
	}

	// 新增：用和读取时一致的解析逻辑做一次完整校验
	if _, err := parser.InitFingerPrintFromData([]byte(req.FileContent)); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "指纹内容校验失败: " + err.Error()})
		return
	}

	// 3. 校验原文件是否存在
	if !isValidName(oldName) || !isValidName(fp.Info.Name) {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "指纹名称非法"})
		return
	}
	oldPath, err := safeJoinPath("data/fingerprints", oldName+".yaml")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "非法路径"})
		return
	}
	if _, err := os.Stat(oldPath); os.IsNotExist(err) {
		c.JSON(http.StatusNotFound, gin.H{"status": 1, "message": "原指纹不存在"})
		return
	}
	newPath, err := safeJoinPath("data/fingerprints", fp.Info.Name+".yaml")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "非法路径"})
		return
	}

	// 4. 校验新文件名是否已存在（且不是原文件）
	if newPath != oldPath {
		if _, err := os.Stat(newPath); err == nil {
			c.JSON(http.StatusConflict, gin.H{"status": 1, "message": "新指纹名称已存在"})
			return
		}
	}

	// 5. 如果新旧文件名不同，删除原文件
	if oldName != fp.Info.Name {
		_ = os.Remove(oldPath) // 删除老文件
	}

	// 6. 写入新内容（新文件名）
	if err := os.WriteFile(newPath, []byte(req.FileContent), 0644); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"status": 1, "message": "文件写入失败: " + err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"status": 0, "message": "修改指纹成功"})
}

// 漏洞库分页+条件查询接口
func HandleListVulnerabilities() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 1. 解析分页和查询参数
		pageStr := c.DefaultQuery("page", "1")
		sizeStr := c.DefaultQuery("size", "20")
		query := strings.ToLower(c.DefaultQuery("q", ""))
		lang := c.DefaultQuery("lang", "zh")
		page, _ := strconv.Atoi(pageStr)
		size, _ := strconv.Atoi(sizeStr)
		if page < 1 {
			page = 1
		}
		if size < 1 {
			size = 10
		}

		engine := vulstruct.NewAdvisoryEngine()
		// load from directory
		dir := "data/vuln"
		if lang == "en" {
			dir = "data/vuln_en"
		}
		err := engine.LoadFromDirectory(dir)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"status": 1, "message": "加载漏洞库失败: " + err.Error()})
			return
		}
		filteredVuls := make([]vulstruct.VersionVul, 0)
		if query == "" {
			filteredVuls = engine.GetAll()
		} else {
			for _, vul := range engine.GetAll() {
				if strings.Contains(strings.ToLower(vul.Info.CVEName), query) {
					filteredVuls = append(filteredVuls, vul)
					continue
				}
				if strings.Contains(strings.ToLower(vul.Info.Summary), query) {
					filteredVuls = append(filteredVuls, vul)
					continue
				}
				if strings.Contains(strings.ToLower(vul.Info.FingerPrintName), query) {
					filteredVuls = append(filteredVuls, vul)
					continue
				}
				if strings.Contains(strings.ToLower(vul.Info.Details), query) {
					filteredVuls = append(filteredVuls, vul)
					continue
				}
				for _, ref := range vul.References {
					if strings.Contains(strings.ToLower(ref), query) {
						filteredVuls = append(filteredVuls, vul)
						break
					}
				}
			}
		}
		// 5. 分页
		total := len(filteredVuls)
		start := (page - 1) * size
		end := start + size
		if start > total {
			start = total
		}
		if end > total {
			end = total
		}
		items := filteredVuls[start:end]

		// 6. 返回
		c.JSON(http.StatusOK, gin.H{
			"status":  0,
			"message": "success",
			"data": gin.H{
				"page":  page,
				"size":  size,
				"total": total,
				"items": items,
			},
		})
	}
}

// 添加漏洞信息（带严格校验）
func HandleCreateVulnerability() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 1. 解析请求体，获取file_content
		type VulnUploadRequest struct {
			FileContent string `json:"file_content" binding:"required"`
		}
		var req VulnUploadRequest
		if err := c.ShouldBindJSON(&req); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "参数解析失败"})
			return
		}

		// 2. 反序列化为vulstruct.VersionVul，校验CVE编号等必填字段
		var vul vulstruct.VersionVul
		if err := yaml.Unmarshal([]byte(req.FileContent), &vul); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "YAML解析失败: " + err.Error()})
			return
		}
		if vul.Info.CVEName == "" {
			c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "CVE编号不能为空"})
			return
		}
		if !isValidName(vul.Info.CVEName) {
			c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "CVE编号非法"})
			return
		}
		if vul.Info.FingerPrintName != "" && !isValidName(vul.Info.FingerPrintName) {
			c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "指纹分类名称非法"})
			return
		}

		// 4. 用vulstruct.NewAdvisoryEngine加载临时文件做完整业务校验
		_, err := vulstruct.ReadVersionVul([]byte(req.FileContent))
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "漏洞内容校验失败: " + err.Error()})
			return
		}

		// 5. 校验通过后，正式写入到目标目录（如已存在则报冲突）
		dir := "data/vuln"
		if vul.Info.FingerPrintName != "" {
			var pathErr error
			dir, pathErr = safeJoinPath(dir, vul.Info.FingerPrintName)
			if pathErr != nil {
				c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "非法路径"})
				return
			}
		}
		if err := os.MkdirAll(dir, 0755); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"status": 1, "message": "创建目录失败: " + err.Error()})
			return
		}
		fileName := strings.ToUpper(vul.Info.CVEName) + ".yaml"
		filePath, err := safeJoinPath(dir, fileName)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "非法路径"})
			return
		}
		if _, err := os.Stat(filePath); err == nil {
			c.JSON(http.StatusConflict, gin.H{"status": 1, "message": "该CVE编号的漏洞已存在"})
			return
		}
		data, err := yaml.Marshal(&vul)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"status": 1, "message": "YAML序列化失败: " + err.Error()})
			return
		}
		if err := os.WriteFile(filePath, data, 0644); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"status": 1, "message": "文件写入失败: " + err.Error()})
			return
		}

		// 6. 返回结果
		c.JSON(http.StatusOK, gin.H{"status": 0, "message": "创建漏洞库成功"})
	}
}

// 编辑漏洞处理函数
func HandleEditVulnerability(c *gin.Context) {
	// 1. 获取原CVE编号
	oldCVE := c.Param("cve")
	if oldCVE == "" {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "CVE编号不能为空"})
		return
	}
	if !isValidName(oldCVE) {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "原CVE编号非法"})
		return
	}

	type VulnUploadRequest struct {
		FileContent string `json:"file_content" binding:"required"`
	}
	var req VulnUploadRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "参数解析失败"})
		return
	}
	// 2. 反序列化为vulstruct.VersionVul，校验CVE编号等必填字段
	var vul vulstruct.VersionVul
	if err := yaml.Unmarshal([]byte(req.FileContent), &vul); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "YAML解析失败: " + err.Error()})
		return
	}
	if vul.Info.CVEName == "" {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "CVE编号不能为空"})
		return
	}
	if !isValidName(vul.Info.CVEName) {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "CVE编号非法"})
		return
	}
	if vul.Info.FingerPrintName != "" && !isValidName(vul.Info.FingerPrintName) {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "指纹分类名称非法"})
		return
	}
	// 4. 用vulstruct.NewAdvisoryEngine加载临时文件做完整业务校验
	_, err := vulstruct.ReadVersionVul([]byte(req.FileContent))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "漏洞内容校验失败: " + err.Error()})
		return
	}

	// 5. 在所有分类目录下查找原文件
	var oldPath string
	found := false
	baseDir := "data/vuln"
	_ = filepath.Walk(baseDir, func(path string, info os.FileInfo, err error) error {
		if err == nil && !info.IsDir() && strings.EqualFold(info.Name(), strings.ToUpper(oldCVE)+".yaml") {
			oldPath = path
			found = true
			return filepath.SkipDir // 找到就停止遍历
		}
		return nil
	})
	if !found {
		c.JSON(http.StatusNotFound, gin.H{"status": 1, "message": "原漏洞不存在"})
		return
	}

	// 6. 生成新文件路径
	newDir := "data/vuln"
	if vul.Info.FingerPrintName != "" {
		newDir, err = safeJoinPath(newDir, vul.Info.FingerPrintName)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "非法路径"})
			return
		}
	}
	if err := os.MkdirAll(newDir, 0755); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"status": 1, "message": "创建目录失败: " + err.Error()})
		return
	}
	newPath, err := safeJoinPath(newDir, strings.ToUpper(vul.Info.CVEName)+".yaml")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "非法路径"})
		return
	}

	// 7. 校验新文件名是否已存在（且不是原文件）
	if newPath != oldPath {
		if _, err := os.Stat(newPath); err == nil {
			c.JSON(http.StatusConflict, gin.H{"status": 1, "message": "新CVE编号的漏洞已存在"})
			return
		}
	}

	// 8. 删除原文件
	if err := os.Remove(oldPath); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"status": 1, "message": "删除原文件失败: " + err.Error()})
		return
	}

	// 9. 写入新内容（新文件名/新目录）
	data, err := yaml.Marshal(&vul)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"status": 1, "message": "YAML序列化失败: " + err.Error()})
		return
	}
	if err := os.WriteFile(newPath, data, 0644); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"status": 1, "message": "文件写入失败: " + err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"status": 0, "message": "修改漏洞成功"})
}

// 批量删除漏洞处理函数
type BatchDeleteVulnRequest struct {
	CVEs []string `json:"cves"`
}

func HandleBatchDeleteVulnerabilities(c *gin.Context) {
	var req BatchDeleteVulnRequest
	if err := c.ShouldBindJSON(&req); err != nil || len(req.CVEs) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "参数解析失败或CVE列表为空"})
		return
	}

	baseDir := "data/vuln"
	var notFound []string
	var failed []string

	for _, cve := range req.CVEs {
		if !isValidName(cve) {
			notFound = append(notFound, cve)
			continue
		}
		found := false
		_ = filepath.Walk(baseDir, func(path string, info os.FileInfo, err error) error {
			if err == nil && !info.IsDir() && strings.EqualFold(info.Name(), strings.ToUpper(cve)+".yaml") {
				// 找到就删除
				if err := os.Remove(path); err != nil {
					failed = append(failed, cve)
				}
				found = true
				return filepath.SkipDir
			}
			return nil
		})
		if !found {
			notFound = append(notFound, cve)
		}
	}

	if len(failed) > 0 {
		c.JSON(500, gin.H{"status": 1, "message": "部分删除失败", "failed": failed})
		return
	}
	if len(notFound) > 0 {
		c.JSON(404, gin.H{"status": 1, "message": "部分CVE未找到", "not_found": notFound})
		return
	}

	c.JSON(http.StatusOK, gin.H{"status": 0, "message": "批量删除成功"})
}

// ================== 评测集管理接口 ==================

// 获取评测集列表，支持分页和名字模糊搜索
func HandleListEvaluations(c *gin.Context) {
	// 1. 解析分页参数
	pageStr := c.DefaultQuery("page", "1")
	sizeStr := c.DefaultQuery("size", "20")
	detail := c.DefaultQuery("detail", "false")
	page, _ := strconv.Atoi(pageStr)
	size, _ := strconv.Atoi(sizeStr)
	if page < 1 {
		page = 1
	}
	if size < 1 {
		size = 10
	}

	// 2. 获取查询参数
	nameQuery := strings.ToLower(c.DefaultQuery("q", ""))

	// 3. 读取 data/eval/ 下所有JSON文件
	var allEvaluations []EvaluationDataset
	root := "data/eval"
	filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
		if err != nil {
			return nil // 忽略错误
		}
		if !d.IsDir() && strings.HasSuffix(d.Name(), ".json") {
			content, readErr := os.ReadFile(path)
			if readErr == nil {
				var eval EvaluationDataset
				err = json.Unmarshal(content, &eval)
				if err != nil {
					log.Error(path, err.Error())
					return err
				}
				// 转换为摘要格式（不包含data字段）
				if detail != "true" {
					eval.Data = nil
				}
				allEvaluations = append(allEvaluations, eval)
			}
		}
		return nil
	})

	// 4. 条件过滤
	var filteredEvaluations []EvaluationDataset
	if nameQuery == "" {
		filteredEvaluations = allEvaluations
	} else {
		for _, eval := range allEvaluations {
			if strings.Contains(strings.ToLower(eval.Name), nameQuery) {
				filteredEvaluations = append(filteredEvaluations, eval)
				continue
			}
			if strings.Contains(strings.ToLower(eval.Description), nameQuery) {
				filteredEvaluations = append(filteredEvaluations, eval)
				continue
			}
			if strings.Contains(strings.ToLower(eval.Author), nameQuery) {
				filteredEvaluations = append(filteredEvaluations, eval)
				continue
			}
			// 搜索标签
			for _, tag := range eval.Tags {
				if strings.Contains(strings.ToLower(tag), nameQuery) {
					filteredEvaluations = append(filteredEvaluations, eval)
					break
				}
			}
		}
	}

	// 5. 默认排序：按照Recommendation降序排列
	sort.Slice(filteredEvaluations, func(i, j int) bool {
		return filteredEvaluations[i].Recommendation > filteredEvaluations[j].Recommendation
	})

	// 6. 分页
	total := len(filteredEvaluations)
	start := (page - 1) * size
	end := start + size
	if start > total {
		start = total
	}
	if end > total {
		end = total
	}
	items := filteredEvaluations[start:end]

	// 7. 返回
	c.JSON(http.StatusOK, gin.H{
		"status":  0,
		"message": "success",
		"data": gin.H{
			"total": total,
			"page":  page,
			"size":  size,
			"items": items,
		},
	})
}

// 获取评测集详情，返回包含data的完整信息
func HandleGetEvaluationDetail(c *gin.Context) {
	// 1. 获取评测集名称
	name := c.Param("name")
	if name == "" {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "评测集名称不能为空"})
		return
	}

	// 2. 验证名称合法性
	if !isValidName(name) {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "评测集名称非法"})
		return
	}

	// 3. 读取评测集文件
	var allEvaluations []EvaluationDataset
	root := "data/eval"
	filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
		if err != nil {
			return nil // 忽略错误
		}
		if !d.IsDir() && strings.HasSuffix(d.Name(), ".json") {
			content, readErr := os.ReadFile(path)
			if readErr == nil {
				var eval EvaluationDataset
				if parseErr := json.Unmarshal(content, &eval); parseErr == nil {
					allEvaluations = append(allEvaluations, eval)
				}
			}
		}
		return nil
	})

	for _, eval := range allEvaluations {
		if eval.Name == name {
			c.JSON(http.StatusOK, gin.H{
				"status":  0,
				"message": "success",
				"data":    eval,
			})
			return
		}
	}

	// 5. 返回完整的评测集信息（包含data字段）
	c.JSON(http.StatusOK, gin.H{
		"status":  0,
		"message": "success",
		"data":    nil,
	})
}

// 创建评测集
func HandleCreateEvaluation(c *gin.Context) {
	// 1. 解析请求体，获取file_content字段
	type EvaluationUploadRequest struct {
		FileContent string `json:"file_content" binding:"required"`
	}
	var req EvaluationUploadRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "参数解析失败"})
		return
	}

	// 2. 解析JSON为EvaluationDataset结构体
	var eval EvaluationDataset
	if err := json.Unmarshal([]byte(req.FileContent), &eval); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "JSON解析失败: " + err.Error()})
		return
	}
	if eval.Name == "" {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "评测集名称不能为空"})
		return
	}

	// 3. 验证数据完整性
	if len(eval.Data) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "评测数据不能为空"})
		return
	}

	// 更新count字段为实际数据条数
	eval.Count = len(eval.Data)

	// 验证数据项
	for i, item := range eval.Data {
		if item.Prompt == "" {
			c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": fmt.Sprintf("第%d条数据的prompt不能为空", i+1)})
			return
		}
	}

	// 4. 检查评测集名称是否已存在
	if !isValidName(eval.Name) {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "评测集名称非法，只允许字母、数字、下划线和横线"})
		return
	}
	jsonPath, err := safeJoinPath("data/eval", eval.Name+".json")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "非法路径"})
		return
	}
	if _, err := os.Stat(jsonPath); err == nil {
		c.JSON(http.StatusConflict, gin.H{"status": 1, "message": "评测集已存在"})
		return
	}

	// 5. 序列化并写入JSON文件
	updatedContent, err := json.MarshalIndent(eval, "", "  ")
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"status": 1, "message": "JSON序列化失败: " + err.Error()})
		return
	}

	if err := os.WriteFile(jsonPath, updatedContent, 0644); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"status": 1, "message": "文件写入失败: " + err.Error()})
		return
	}

	// 6. 返回精简响应
	c.JSON(http.StatusOK, gin.H{"status": 0, "message": "创建评测集成功"})
}

// 编辑评测集处理函数
func HandleEditEvaluation(c *gin.Context) {
	// 1. 获取原评测集名称
	oldName := c.Param("name")
	if oldName == "" {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "评测集名称不能为空"})
		return
	}

	type EvaluationUploadRequest struct {
		FileContent string `json:"file_content" binding:"required"`
	}
	var req EvaluationUploadRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "参数解析失败"})
		return
	}

	// 2. 解析JSON为EvaluationDataset结构体
	var eval EvaluationDataset
	if err := json.Unmarshal([]byte(req.FileContent), &eval); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "JSON解析失败: " + err.Error()})
		return
	}
	if eval.Name == "" {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "评测集名称不能为空"})
		return
	}

	// 验证数据完整性
	if len(eval.Data) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "评测数据不能为空"})
		return
	}

	// 更新count字段为实际数据条数
	eval.Count = len(eval.Data)

	// 验证数据项
	for i, item := range eval.Data {
		if item.Prompt == "" {
			c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": fmt.Sprintf("第%d条数据的prompt不能为空", i+1)})
			return
		}
	}

	// 3. 校验原文件是否存在
	if !isValidName(oldName) || !isValidName(eval.Name) {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "评测集名称非法，只允许字母、数字、下划线和横线"})
		return
	}
	oldPath, err := safeJoinPath("data/eval", oldName+".json")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "非法路径"})
		return
	}
	if _, err := os.Stat(oldPath); os.IsNotExist(err) {
		c.JSON(http.StatusNotFound, gin.H{"status": 1, "message": "原评测集不存在"})
		return
	}
	newPath, err := safeJoinPath("data/eval", eval.Name+".json")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "非法路径"})
		return
	}

	// 4. 校验新文件名是否已存在（且不是原文件）
	if newPath != oldPath {
		if _, err := os.Stat(newPath); err == nil {
			c.JSON(http.StatusConflict, gin.H{"status": 1, "message": "新评测集名称已存在"})
			return
		}
	}

	// 5. 如果新旧文件名不同，删除原文件
	if oldName != eval.Name {
		_ = os.Remove(oldPath) // 删除老文件
	}

	// 6. 序列化并写入新内容（新文件名）
	updatedContent, err := json.MarshalIndent(eval, "", "  ")
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"status": 1, "message": "JSON序列化失败: " + err.Error()})
		return
	}

	if err := os.WriteFile(newPath, updatedContent, 0644); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"status": 1, "message": "文件写入失败: " + err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"status": 0, "message": "修改评测集成功"})
}

// 批量删除评测集处理函数
type BatchDeleteEvaluationRequest struct {
	Names []string `json:"names"`
}

func HandleDeleteEvaluation(c *gin.Context) {
	var req BatchDeleteEvaluationRequest
	if err := c.ShouldBindJSON(&req); err != nil || len(req.Names) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "参数错误", "data": nil})
		return
	}

	for _, name := range req.Names {
		// 使用已存在的合法性校验函数防止路径遍历攻击
		if !isValidName(name) {
			c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "评测集名称非法，只允许字母、数字、下划线和横线"})
			return
		}
		jsonPath, pathErr := safeJoinPath("data/eval", name+".json")
		if pathErr != nil {
			c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "非法路径"})
			return
		}
		if _, err := os.Stat(jsonPath); os.IsNotExist(err) {
			c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "评测集不存在"})
			return
		}
		if err := os.Remove(jsonPath); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"status": 1, "message": "删除失败: " + err.Error()})
			return
		}
	}
	c.JSON(http.StatusOK, gin.H{
		"status":  0,
		"message": "ok",
	})
}
