package skilltools

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"

	"github.com/enmanuel/agents/pkg/skills"
	shellskills "github.com/enmanuel/agents/shell/skills"
	"github.com/enmanuel/agents/tools"
)

// NewSkillSearch creates a skill_search tool that finds relevant skills.
func NewSkillSearch(loader *shellskills.Loader, categories []string) tools.Tool {
	return tools.Tool{
		Def: tools.Def{
			Name:        "skill_search",
			Description: "Search for skills relevant to a query. Returns a list of skills with their names, descriptions, and relevance scores. Use this when you need to find a skill to help with a task.",
			Parameters: []tools.Param{
				{Name: "query", Type: "string", Description: "Search query describing the task or capability needed", Required: true},
			},
		},
		Exec: func(ctx context.Context, args map[string]any) tools.Result {
			query := tools.GetString(args, "query")
			if query == "" {
				return tools.Result{Err: fmt.Errorf("query is required")}
			}

			// Load all skill metadata
			metas, err := loader.LoadMeta()
			if err != nil {
				return tools.Result{Err: fmt.Errorf("load skills metadata: %w", err)}
			}

			// Filter by categories if configured
			metas = skills.FilterByCategory(metas, categories)

			// Match skills to query
			matches := skills.Match(query, metas)

			if len(matches) == 0 {
				return tools.Result{Output: "No skills found matching the query."}
			}

			// Format output
			var lines []string
			lines = append(lines, fmt.Sprintf("Found %d relevant skill(s):\n", len(matches)))
			for i, match := range matches {
				if i >= 5 {
					break // limit to top 5
				}
				lines = append(lines, fmt.Sprintf("%d. **%s** (category: %s, confidence: %.2f)",
					i+1, match.Skill.Name, match.Skill.Category, match.Confidence))
				lines = append(lines, fmt.Sprintf("   %s\n", match.Skill.Description))
			}

			return tools.Result{Output: strings.Join(lines, "\n")}
		},
	}
}

// NewSkillLoad creates a skill_load tool that loads full instructions for a skill.
func NewSkillLoad(loader *shellskills.Loader) tools.Tool {
	return tools.Tool{
		Def: tools.Def{
			Name:        "skill_load",
			Description: "Load the complete instructions for a skill. This returns the full markdown content of the skill, which you should follow to complete the task. Use this after finding a skill with skill_search.",
			Parameters: []tools.Param{
				{Name: "skill_name", Type: "string", Description: "Name of the skill to load", Required: true},
			},
		},
		Exec: func(ctx context.Context, args map[string]any) tools.Result {
			skillName := tools.GetString(args, "skill_name")
			if skillName == "" {
				return tools.Result{Err: fmt.Errorf("skill_name is required")}
			}

			skill, err := loader.LoadSkill(skillName)
			if err != nil {
				return tools.Result{Err: fmt.Errorf("load skill: %w", err)}
			}

			// Format output with metadata + instructions
			var output strings.Builder
			output.WriteString(fmt.Sprintf("# Skill: %s\n\n", skill.Meta.Name))
			output.WriteString(fmt.Sprintf("**Category**: %s\n\n", skill.Meta.Category))
			output.WriteString(fmt.Sprintf("**Description**: %s\n\n", skill.Meta.Description))

			if len(skill.Scripts) > 0 {
				output.WriteString(fmt.Sprintf("**Scripts available**: %s\n", strings.Join(skill.Scripts, ", ")))
			}
			if len(skill.References) > 0 {
				output.WriteString(fmt.Sprintf("**References available**: %s\n", strings.Join(skill.References, ", ")))
			}
			if len(skill.Templates) > 0 {
				output.WriteString(fmt.Sprintf("**Templates available**: %s\n", strings.Join(skill.Templates, ", ")))
			}

			output.WriteString("\n---\n\n")
			output.WriteString(skill.Instructions)

			return tools.Result{Output: output.String()}
		},
	}
}

// NewSkillReadResource creates a skill_read_resource tool that reads a specific resource.
func NewSkillReadResource(loader *shellskills.Loader) tools.Tool {
	return tools.Tool{
		Def: tools.Def{
			Name:        "skill_read_resource",
			Description: "Read a specific resource file from a skill (script, reference doc, template, or asset). Use this to load additional documentation or code referenced in the skill instructions.",
			Parameters: []tools.Param{
				{Name: "skill_name", Type: "string", Description: "Name of the skill", Required: true},
				{Name: "resource_path", Type: "string", Description: "Path to the resource relative to the skill directory (e.g., 'scripts/deploy.sh', 'references/api.md')", Required: true},
			},
		},
		Exec: func(ctx context.Context, args map[string]any) tools.Result {
			skillName := tools.GetString(args, "skill_name")
			resourcePath := tools.GetString(args, "resource_path")

			if skillName == "" || resourcePath == "" {
				return tools.Result{Err: fmt.Errorf("skill_name and resource_path are required")}
			}

			content, err := loader.ReadResource(skillName, resourcePath)
			if err != nil {
				return tools.Result{Err: fmt.Errorf("read resource: %w", err)}
			}

			return tools.Result{Output: content}
		},
	}
}

// NewSkillRunScript creates a skill_run_script tool that executes a skill script.
func NewSkillRunScript(loader *shellskills.Loader, executor *shellskills.Executor) tools.Tool {
	return tools.Tool{
		Def: tools.Def{
			Name:        "skill_run_script",
			Description: "Execute a script from a skill with the given arguments. The script must be in the skill's scripts/ directory and use an allowed interpreter. Returns the script output.",
			Parameters: []tools.Param{
				{Name: "skill_name", Type: "string", Description: "Name of the skill", Required: true},
				{Name: "script_name", Type: "string", Description: "Name of the script file (e.g., 'deploy.sh')", Required: true},
				{Name: "args", Type: "array", Description: "Array of arguments to pass to the script", Required: false},
			},
		},
		Exec: func(ctx context.Context, args map[string]any) tools.Result {
			skillName := tools.GetString(args, "skill_name")
			scriptName := tools.GetString(args, "script_name")

			if skillName == "" || scriptName == "" {
				return tools.Result{Err: fmt.Errorf("skill_name and script_name are required")}
			}

			// Parse args array
			var scriptArgs []string
			if argsRaw, ok := args["args"]; ok {
				argsJSON, _ := json.Marshal(argsRaw)
				_ = json.Unmarshal(argsJSON, &scriptArgs)
			}

			// Load skill to get base path
			skill, err := loader.LoadSkill(skillName)
			if err != nil {
				return tools.Result{Err: fmt.Errorf("load skill: %w", err)}
			}

			// Verify script exists
			scriptFound := false
			for _, s := range skill.Scripts {
				if s == scriptName {
					scriptFound = true
					break
				}
			}
			if !scriptFound {
				return tools.Result{Err: fmt.Errorf("script not found in skill: %s", scriptName)}
			}

			// Execute script
			scriptPath := fmt.Sprintf("%s/scripts/%s", skill.BasePath, scriptName)
			output, err := executor.Run(ctx, scriptPath, scriptArgs)
			if err != nil {
				return tools.Result{
					Output: output,
					Err:    fmt.Errorf("script execution failed: %w", err),
				}
			}

			return tools.Result{Output: output}
		},
	}
}
