package room

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/sdaduanbilei/agent-team/internal/llm"
	"github.com/sdaduanbilei/agent-team/internal/skill"
	"github.com/sdaduanbilei/agent-team/internal/store"
)

// buildWorkflowStep 根据 workspace 中已存在的文件，构建 phase-aware 的工作流进度上下文
func (r *Room) buildWorkflowStep() string {
	if r.projectTemplate == nil {
		return ""
	}
	var completed []string
	var pending []string
	minPendingPhase := 999

	for _, f := range r.projectTemplate.Files {
		if f.IsDir || f.Dynamic {
			continue
		}
		fpath := filepath.Join(r.Dir, "workspace", f.Path)
		if _, err := os.Stat(fpath); err == nil {
			completed = append(completed, f.Path)
		} else {
			pending = append(pending, fmt.Sprintf("%s (@%s, phase:%d)", f.Path, f.Owner, f.Phase))
			if f.Phase < minPendingPhase {
				minPendingPhase = f.Phase
			}
		}
	}

	var sb strings.Builder
	sb.WriteString("<workflow_progress>\n")
	sb.WriteString("已完成的文件：\n")
	for _, f := range completed {
		sb.WriteString(fmt.Sprintf("  [done] %s\n", f))
	}
	sb.WriteString("待产出的文件：\n")
	for _, f := range pending {
		sb.WriteString(fmt.Sprintf("  [todo] %s\n", f))
	}
	sb.WriteString("</workflow_progress>\n\n")

	if minPendingPhase < 999 {
		sb.WriteString("<next_action>\n当前阶段：前置材料准备\n")
		sb.WriteString("待产出的前置材料：\n")
		for _, f := range r.projectTemplate.Files {
			if f.IsDir || f.Dynamic {
				continue
			}
			fpath := filepath.Join(r.Dir, "workspace", f.Path)
			if _, err := os.Stat(fpath); err == nil {
				continue
			}
			if f.Owner == r.master.Config.Name {
				sb.WriteString(fmt.Sprintf("- 你负责：%s（系统将自动发起文档调用，你不需要在此输出文档正文）\n", f.Path))
			} else {
				sb.WriteString(fmt.Sprintf("- 分配给 @%s：%s\n", f.Owner, f.Path))
			}
		}
		sb.WriteString("</next_action>\n")
		sb.WriteString("请安排产出待完成的前置材料。你负责的文件由系统自动发起独立调用，不要在回复中输出完整文档正文。")
	} else if dynDir, dynOwner, dynPhase := r.getDynamicFileInfo(); dynDir != "" {
		// 所有静态文件已完成，进入动态章节写作阶段
		existingChapters := r.listChapterFiles(dynDir)
		sb.WriteString(fmt.Sprintf("<next_action>\n当前阶段：phase %d — 章节写作\n", dynPhase))
		sb.WriteString(fmt.Sprintf("负责人：%s\n", dynOwner))
		if len(existingChapters) > 0 {
			sb.WriteString("已完成章节：\n")
			for _, ch := range existingChapters {
				sb.WriteString(fmt.Sprintf("  [done] %s\n", ch))
			}
		}
		isMasterDyn := r.master != nil && dynOwner == r.master.Config.Name
		if isMasterDyn {
			sb.WriteString("系统将自动发起章节文档调用，你不需要在回复中输出章节正文。\n")
			sb.WriteString("请简短说明接下来要写哪一章，系统会自动发起文档调用。\n")
		} else {
			sb.WriteString(fmt.Sprintf("请用 @%s 分配章节写作任务，任务中注明章节序号和标题。\n", dynOwner))
			sb.WriteString("每次分配一章，写完后你会收到完成通知，再安排下一章。\n")
		}
		sb.WriteString("</next_action>\n")
	} else {
		sb.WriteString("所有模板文件已完成。请根据你的 AGENT.md 工作流程决定下一步行动（如向用户交付、或进入下一阶段）。")
	}

	return sb.String()
}

// currentMinPhase 返回当前最小未完成 phase
func (r *Room) currentMinPhase() int {
	if r.projectTemplate == nil {
		return 0
	}
	minPhase := 999
	for _, f := range r.projectTemplate.Files {
		if f.IsDir || f.Dynamic {
			continue
		}
		fpath := filepath.Join(r.Dir, "workspace", f.Path)
		if _, err := os.Stat(fpath); os.IsNotExist(err) {
			if f.Phase < minPhase {
				minPhase = f.Phase
			}
		}
	}
	if minPhase == 999 {
		return 0
	}
	return minPhase
}

// validatePhaseAssignments 轻量校验：只在分配写手（phase:2 任务）时检查前置材料（phase:1）是否全部就绪
// 返回被阻止的分配及原因
func (r *Room) validatePhaseAssignments(assignments map[string]string) map[string]string {
	if r.projectTemplate == nil {
		return nil
	}

	// 检查所有 phase:1 文件是否已完成
	allPhase1Done := true
	var pendingPhase1 []string
	for _, f := range r.projectTemplate.Files {
		if f.IsDir || f.Dynamic || f.Phase != 1 {
			continue
		}
		fpath := filepath.Join(r.Dir, "workspace", f.Path)
		if _, err := os.Stat(fpath); os.IsNotExist(err) {
			allPhase1Done = false
			pendingPhase1 = append(pendingPhase1, fmt.Sprintf("%s(@%s)", f.Path, f.Owner))
		}
	}
	if allPhase1Done {
		return nil // 前置材料全部完成，不阻止任何分配
	}

	// 只阻止 phase:2（写手/章节）的分配，phase:1 内部不互相阻止
	blocked := make(map[string]string)
	for name := range assignments {
		targetFile := r.findMemberTargetFile(name)
		if targetFile == nil {
			continue
		}
		if targetFile.Phase > 1 {
			blocked[name] = fmt.Sprintf(
				"《%s》属于 phase:%d，但前置材料尚未全部完成：%s",
				targetFile.Path, targetFile.Phase,
				strings.Join(pendingPhase1, "、"))
		}
	}
	return blocked
}

// allStaticFilesDone 检查所有非动态模板文件是否都已完成
func (r *Room) allStaticFilesDone() bool {
	if r.projectTemplate == nil {
		return false
	}
	for _, f := range r.projectTemplate.Files {
		if f.IsDir || f.Dynamic {
			continue
		}
		fpath := filepath.Join(r.Dir, "workspace", f.Path)
		if _, err := os.Stat(fpath); os.IsNotExist(err) {
			return false
		}
	}
	return true
}

// getDynamicFileInfo 返回动态文件的目录名、负责人和阶段号
func (r *Room) getDynamicFileInfo() (dir, owner string, phase int) {
	if r.projectTemplate == nil {
		return "", "", 0
	}
	// 找到 dynamic 条目，以及它前面的目录条目
	lastDir := ""
	for _, f := range r.projectTemplate.Files {
		if f.IsDir {
			lastDir = strings.TrimSuffix(f.Path, "/")
			continue
		}
		if f.Dynamic {
			if lastDir == "" {
				lastDir = "chapters"
			}
			return lastDir, f.Owner, f.Phase
		}
	}
	return "", "", 0
}

// listChapterFiles 列出 workspace 根目录下的章节文件（非模板静态文件的 .md 文件）
func (r *Room) listChapterFiles(dir string) []string {
	wsDir := filepath.Join(r.Dir, "workspace")
	entries, err := os.ReadDir(wsDir)
	if err != nil {
		return nil
	}
	// 收集模板静态文件名，用于排除
	staticFiles := make(map[string]bool)
	if r.projectTemplate != nil {
		for _, f := range r.projectTemplate.Files {
			if !f.IsDir && !f.Dynamic {
				staticFiles[f.Path] = true
			}
		}
	}
	var files []string
	for _, e := range entries {
		if e.IsDir() || !strings.HasSuffix(e.Name(), ".md") {
			continue
		}
		if staticFiles[e.Name()] {
			continue
		}
		files = append(files, e.Name())
	}
	return files
}

// masterChapterFileCall 为 master 发起一次章节文档调用
func (r *Room) masterChapterFileCall(ctx context.Context, masterMsgs *[]llm.Message, dir string, chapterHint string) {
	r.setStatus(StatusWorking, r.master.Config.Name, "正在编写章节...")
	r.emit(Event{Type: EvtFileWorking, Agent: r.master.Config.Name, Filename: "章节", Title: "章节"})

	existingChapters := r.listChapterFiles(dir)
	var existingList string
	if len(existingChapters) > 0 {
		existingList = "\n已完成的章节：\n"
		for _, ch := range existingChapters {
			existingList += fmt.Sprintf("- %s\n", ch)
		}
	}

	filePrompt := r.Prompt.Render("file_call_chapter", map[string]string{
		"Dir":              dir,
		"ExistingChapters": existingList,
	})

	if chapterHint != "" {
		filePrompt += "\n\n你之前的规划：" + chapterHint
	}

	if wsCtx := r.buildWorkspaceContext(); wsCtx != "" {
		filePrompt += "\n\n" + wsCtx
	}

	fileLLMMsg := llm.NewMsg("user", filePrompt)
	*masterMsgs = append(*masterMsgs, fileLLMMsg)

	reply, usage, err := r.master.ChatWithUsage(ctx, *masterMsgs, nil)
	if err != nil {
		log.Printf("[room %s] master chapter file call error: %v", r.Config.Name, err)
		return
	}
	r.emitUsage(r.master.Config.Name, usage)

	content := strings.TrimSpace(reply)

	// 从内容中提取章节标题作为文件名（直接保存到 workspace 根目录，不创建子文件夹）
	chapterFilename := r.extractChapterFilename(content, dir)

	if !strings.HasPrefix(content, "# ") {
		content = "# " + strings.TrimSuffix(chapterFilename, ".md") + "\n\n" + content
	}

	r.saveWorkspace(chapterFilename, content)
	docName := strings.TrimSuffix(chapterFilename, ".md")
	r.emit(Event{Type: EvtArtifact, Agent: r.master.Config.Name, Filename: chapterFilename, Title: docName})

	if r.Store != nil {
		r.Store.InsertMessage(&store.Message{
			RoomID: r.Config.Name, Agent: r.master.Config.Name, Role: "master",
			Content: docName, Filename: chapterFilename, PartType: "document",
			GroupID: &r.currentGroupID,
		})
	}

	r.emit(Event{Type: EvtFileDone, Agent: r.master.Config.Name, Filename: chapterFilename, Title: docName})

	// 发送完成状态到聊天
	statusMsg := fmt.Sprintf("《%s》已完成，保存到 %s。", docName, chapterFilename)
	r.emit(Event{Type: EvtAgentMessage, Agent: r.master.Config.Name, Role: "master", Content: statusMsg, NoStore: true})
	if r.Store != nil {
		r.Store.InsertMessage(&store.Message{
			RoomID: r.Config.Name, Agent: r.master.Config.Name, Role: "master",
			Content: statusMsg, PartType: "text",
			GroupID: &r.currentGroupID,
		})
	}

	assistantMsg := llm.NewMsg("assistant", reply)
	*masterMsgs = append(*masterMsgs, assistantMsg)
	r.historyMu.Lock()
	r.masterHistory = append(r.masterHistory, fileLLMMsg, assistantMsg)
	r.historyMu.Unlock()
	r.AppendHistory("master", r.master.Config.Name, reply)
}

// extractChapterFilename 从章节内容中提取文件名
func (r *Room) extractChapterFilename(content, dir string) string {
	title := extractTitle(content)
	if title == "" {
		// 按已有章节数自增
		existing := r.listChapterFiles(dir)
		return fmt.Sprintf("第%d章.md", len(existing)+1)
	}
	// 清理标题中的特殊字符，作为文件名
	title = strings.ReplaceAll(title, " ", "-")
	title = strings.ReplaceAll(title, "/", "-")
	title = strings.ReplaceAll(title, "\\", "-")
	// 先去除可能已有的 .md 后缀，再统一添加，避免双重扩展名
	title = strings.TrimSuffix(title, ".md")
	title += ".md"
	return title
}

// buildProjectContext 构建项目模板上下文，注入到 agent system prompt
func (r *Room) buildProjectContext(agentName string) string {
	if r.projectTemplate == nil {
		return ""
	}
	isMaster := r.master != nil && agentName == r.master.Config.Name

	var sb strings.Builder
	sb.WriteString("<project_template>\n")
	sb.WriteString("项目文件结构（系统自动管理文件保存）：\n\n")
	for _, f := range r.projectTemplate.Files {
		if f.IsDir {
			sb.WriteString(fmt.Sprintf("  %s (目录)\n", f.Path))
			continue
		}
		if f.Dynamic {
			sb.WriteString(fmt.Sprintf("  ... @%s phase:%d (动态扩展)\n", f.Owner, f.Phase))
			continue
		}
		marker := ""
		if f.Owner == agentName {
			marker = " ← 你负责"
		}
		sb.WriteString(fmt.Sprintf("  %s  @%s  phase:%d%s\n", f.Path, f.Owner, f.Phase, marker))
	}

	if isMaster {
		// master：文档通过系统 file call 产出，聊天中只做规划和分配
		sb.WriteString("\n" + r.Prompt.R("master_output_spec") + "\n")
	} else {
		// 成员：最终回复输出文档正文
		sb.WriteString("\n" + r.Prompt.R("output_spec") + "\n")
		sb.WriteString("系统会自动保存到对应文件。不要在文档中夹杂状态描述或对话内容。\n")
	}
	sb.WriteString("</project_template>")
	return sb.String()
}

// matchTemplateFile 按标题匹配模板文件。
func (r *Room) matchTemplateFile(title string) *ProjectFile {
	if r.projectTemplate == nil || title == "" {
		return nil
	}
	for i := range r.projectTemplate.Files {
		f := &r.projectTemplate.Files[i]
		if f.IsDir || f.Dynamic {
			continue
		}
		fname := strings.TrimSuffix(f.Path, ".md")
		if fname == title {
			return f
		}
	}
	for i := range r.projectTemplate.Files {
		f := &r.projectTemplate.Files[i]
		if f.IsDir || f.Dynamic {
			continue
		}
		fname := strings.TrimSuffix(f.Path, ".md")
		if strings.HasPrefix(title, fname) {
			return f
		}
	}
	for i := range r.projectTemplate.Files {
		f := &r.projectTemplate.Files[i]
		if f.IsDir || f.Dynamic {
			continue
		}
		base := strings.TrimSuffix(f.Path, ".md")
		if strings.Contains(base, title) || strings.Contains(title, base) {
			return f
		}
		keywords := strings.FieldsFunc(base, func(r rune) bool { return r == '与' || r == '和' || r == '·' })
		for _, kw := range keywords {
			if len([]rune(kw)) >= 2 && strings.Contains(title, kw) {
				return f
			}
		}
	}
	return nil
}

// findOwnerFiles 查找某 agent 负责的所有文件
func (r *Room) findOwnerFiles(agentName string) []ProjectFile {
	if r.projectTemplate == nil {
		return nil
	}
	var files []ProjectFile
	for _, f := range r.projectTemplate.Files {
		if f.Owner == agentName && !f.IsDir && !f.Dynamic {
			files = append(files, f)
		}
	}
	return files
}

// findPendingMasterFiles 查找 master 负责的所有待产出文件（不限 phase）
func (r *Room) findPendingMasterFiles() []ProjectFile {
	if r.projectTemplate == nil {
		return nil
	}
	var files []ProjectFile
	for _, f := range r.projectTemplate.Files {
		if f.IsDir || f.Dynamic || f.Owner != r.master.Config.Name {
			continue
		}
		fpath := filepath.Join(r.Dir, "workspace", f.Path)
		if _, err := os.Stat(fpath); os.IsNotExist(err) {
			files = append(files, f)
		}
	}
	return files
}

// masterFileCall 为 master 发起一次独立的文档产出调用（file call）。
// 输出直接保存到 workspace，不进入聊天记录。
func (r *Room) masterFileCall(ctx context.Context, masterMsgs *[]llm.Message, file ProjectFile) {
	docName := strings.TrimSuffix(file.Path, ".md")
	r.setStatus(StatusWorking, r.master.Config.Name, fmt.Sprintf("正在编写《%s》...", docName))
	r.emit(Event{Type: EvtFileWorking, Agent: r.master.Config.Name, Filename: file.Path, Title: docName})

	filePrompt := r.Prompt.Render("file_call_master", map[string]string{
		"DocName":  docName,
		"FilePath": file.Path,
	})

	if wsCtx := r.buildWorkspaceContext(); wsCtx != "" {
		filePrompt += "\n\n" + wsCtx
	}

	fileLLMMsg := llm.NewMsg("user", filePrompt)
	*masterMsgs = append(*masterMsgs, fileLLMMsg)

	reply, usage, err := r.master.ChatWithUsage(ctx, *masterMsgs, nil)
	if err != nil {
		log.Printf("[room %s] master file call error for %s: %v", r.Config.Name, file.Path, err)
		return
	}
	r.emitUsage(r.master.Config.Name, usage)

	content := strings.TrimSpace(reply)
	if !strings.HasPrefix(content, "# ") {
		content = "# " + docName + "\n\n" + content
	}

	r.saveWorkspace(file.Path, content)
	r.emit(Event{Type: EvtArtifact, Agent: r.master.Config.Name, Filename: file.Path, Title: docName})

	if r.Store != nil {
		r.Store.InsertMessage(&store.Message{
			RoomID: r.Config.Name, Agent: r.master.Config.Name, Role: "master",
			Content: docName, Filename: file.Path, PartType: "document",
			GroupID: &r.currentGroupID,
		})
	}

	r.emit(Event{Type: EvtFileDone, Agent: r.master.Config.Name, Filename: file.Path, Title: docName})

	assistantMsg := llm.NewMsg("assistant", reply)
	*masterMsgs = append(*masterMsgs, assistantMsg)
	r.historyMu.Lock()
	r.masterHistory = append(r.masterHistory, fileLLMMsg, assistantMsg)
	r.historyMu.Unlock()
	r.AppendHistory("master", r.master.Config.Name, reply)
}

// findMemberTargetFile 查找成员当前应产出的目标文件（用于 file call）
func (r *Room) findMemberTargetFile(name string) *ProjectFile {
	ownerFiles := r.findOwnerFiles(name)
	if len(ownerFiles) == 0 {
		log.Printf("[room %s] findMemberTargetFile(%s): no owner files found", r.Config.Name, name)
		return nil
	}
	log.Printf("[room %s] findMemberTargetFile(%s): found %d owner files", r.Config.Name, name, len(ownerFiles))
	if len(ownerFiles) == 1 {
		log.Printf("[room %s] findMemberTargetFile(%s): → %s", r.Config.Name, name, ownerFiles[0].Path)
		return &ownerFiles[0]
	}
	for i := range ownerFiles {
		fpath := filepath.Join(r.Dir, "workspace", ownerFiles[i].Path)
		if _, err := os.Stat(fpath); os.IsNotExist(err) {
			log.Printf("[room %s] findMemberTargetFile(%s): → %s (not yet exists)", r.Config.Name, name, ownerFiles[i].Path)
			return &ownerFiles[i]
		}
	}
	result := &ownerFiles[len(ownerFiles)-1]
	log.Printf("[room %s] findMemberTargetFile(%s): → %s (fallback last)", r.Config.Name, name, result.Path)
	return result
}

// parseMasterUpdateIntent 从 master 回复中解析更新文件的意图，返回需要更新的已存在模板文件
func (r *Room) parseMasterUpdateIntent(reply string) []ProjectFile {
	if r.projectTemplate == nil {
		return nil
	}
	// 检查 master 回复中是否提到了更新/修改某个已存在的模板文件
	// 关键词：已更新、已修改、已调整、更新了、修改了
	updateKeywords := []string{"已更新", "已修改", "已调整", "更新了", "修改了", "修改为", "改为", "调整为"}
	hasUpdateIntent := false
	for _, kw := range updateKeywords {
		if strings.Contains(reply, kw) {
			hasUpdateIntent = true
			break
		}
	}
	if !hasUpdateIntent {
		return nil
	}

	var files []ProjectFile
	seen := make(map[string]bool)
	for _, f := range r.projectTemplate.Files {
		if f.IsDir || f.Dynamic || f.Owner != r.master.Config.Name {
			continue
		}
		fpath := filepath.Join(r.Dir, "workspace", f.Path)
		if _, err := os.Stat(fpath); err != nil {
			continue // 文件不存在，不是更新场景
		}
		// 检查 master 是否在回复中提到了这个文件
		docName := strings.TrimSuffix(f.Path, ".md")
		if strings.Contains(reply, docName) || strings.Contains(reply, "《"+docName+"》") {
			if !seen[f.Path] {
				seen[f.Path] = true
				files = append(files, f)
			}
		}
	}
	return files
}

// masterFileUpdateCall 为 master 发起一次文件更新调用（更新已存在的文件）
func (r *Room) masterFileUpdateCall(ctx context.Context, masterMsgs *[]llm.Message, file ProjectFile, updateHint string) {
	docName := strings.TrimSuffix(file.Path, ".md")
	r.setStatus(StatusWorking, r.master.Config.Name, fmt.Sprintf("正在更新《%s》...", docName))
	r.emit(Event{Type: EvtFileWorking, Agent: r.master.Config.Name, Filename: file.Path, Title: docName})

	// 读取现有文件内容
	currentContent := ""
	fpath := filepath.Join(r.Dir, "workspace", file.Path)
	if data, err := os.ReadFile(fpath); err == nil {
		currentContent = string(data)
	}

	filePrompt := r.Prompt.Render("file_call_master_update", map[string]string{
		"DocName":        docName,
		"FilePath":       file.Path,
		"UpdateHint":     updateHint,
		"CurrentContent": currentContent,
	})

	fileLLMMsg := llm.NewMsg("user", filePrompt)
	*masterMsgs = append(*masterMsgs, fileLLMMsg)

	reply, usage, err := r.master.ChatWithUsage(ctx, *masterMsgs, nil)
	if err != nil {
		log.Printf("[room %s] master file update call error for %s: %v", r.Config.Name, file.Path, err)
		return
	}
	r.emitUsage(r.master.Config.Name, usage)

	content := strings.TrimSpace(reply)
	if !strings.HasPrefix(content, "# ") {
		content = "# " + docName + "\n\n" + content
	}

	r.saveWorkspace(file.Path, content)
	r.emit(Event{Type: EvtArtifact, Agent: r.master.Config.Name, Filename: file.Path, Title: docName})

	if r.Store != nil {
		r.Store.InsertMessage(&store.Message{
			RoomID: r.Config.Name, Agent: r.master.Config.Name, Role: "master",
			Content: docName, Filename: file.Path, PartType: "document",
			GroupID: &r.currentGroupID,
		})
	}

	r.emit(Event{Type: EvtFileDone, Agent: r.master.Config.Name, Filename: file.Path, Title: docName})

	// 发送完成状态到聊天
	statusMsg := fmt.Sprintf("《%s》已更新。", docName)
	r.emit(Event{Type: EvtAgentMessage, Agent: r.master.Config.Name, Role: "master", Content: statusMsg, NoStore: true})
	if r.Store != nil {
		r.Store.InsertMessage(&store.Message{
			RoomID: r.Config.Name, Agent: r.master.Config.Name, Role: "master",
			Content: statusMsg, PartType: "text",
			GroupID: &r.currentGroupID,
		})
	}

	assistantMsg := llm.NewMsg("assistant", reply)
	*masterMsgs = append(*masterMsgs, assistantMsg)
	r.historyMu.Lock()
	r.masterHistory = append(r.masterHistory, fileLLMMsg, assistantMsg)
	r.historyMu.Unlock()
	r.AppendHistory("master", r.master.Config.Name, reply)
}

// buildSkillSummary 为 master 构建简要的 skill 清单
func (r *Room) buildSkillSummary() string {
	if len(r.skillMeta) == 0 {
		return ""
	}
	var sb strings.Builder
	sb.WriteString("<available_skills>\n")
	sb.WriteString("以下工具可供团队成员使用，你可以在分配任务时提示成员使用对应的工具：\n")
	for _, m := range r.skillMeta {
		fmt.Fprintf(&sb, "  - %s: %s\n", m.Name, m.Description)
	}
	sb.WriteString("</available_skills>")
	return sb.String()
}

// buildTeamXML 构建团队成员 XML 上下文
func (r *Room) buildTeamXML() string {
	var sb strings.Builder
	sb.WriteString("<team_members>\n")
	for name, a := range r.members {
		fmt.Fprintf(&sb, "  <member>\n    <name>%s</name>\n    <description>%s</description>\n  </member>\n", name, a.Config.Description)
	}
	sb.WriteString("</team_members>")
	return sb.String()
}

// executeToolCall 执行 tool call，返回执行结果
func (r *Room) executeToolCall(tc llm.ToolCall) string {
	var args struct {
		Command string `json:"command"`
	}
	if err := json.Unmarshal([]byte(tc.Function.Arguments), &args); err != nil {
		return fmt.Sprintf("参数解析错误: %v", err)
	}

	skillPath := skill.SkillPathByToolName(r.skillMeta, tc.Function.Name)
	skillsRoot := filepath.Dir(skillPath)
	if skillPath == "" {
		skillsRoot = "skills"
	}
	if abs, err := filepath.Abs(skillsRoot); err == nil {
		skillsRoot = abs
	}

	log.Printf("[tool] 执行: %s, 命令: %s", tc.Function.Name, args.Command)

	cmd := exec.Command("bash", "-c", args.Command)
	cmd.Env = append(os.Environ(), "SKILLS_ROOT="+skillsRoot)
	cmd.Dir = r.Dir

	output, err := cmd.CombinedOutput()
	result := string(output)
	if err != nil {
		result = fmt.Sprintf("命令执行错误: %v\n输出:\n%s", err, result)
	}

	if len(result) > 10000 {
		result = result[:10000] + "\n... (输出已截断)"
	}

	log.Printf("[tool] 结果 (%d 字符): %.200s", len(result), result)
	return result
}
