package handlers

import (
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"github.com/gin-gonic/gin"
	"sproutclaw-web/internal/config"
)

// RegisterPrompts registers prompt-template CRUD routes.
func RegisterPrompts(r *gin.RouterGroup, cfg *config.Config) {
	dir := cfg.PromptsDir
	r.GET("/prompts", handleListPrompts(dir))
	r.GET("/prompts/file", handleGetPromptFile(dir))
	r.POST("/prompts/file", handleSavePromptFile(dir))
	r.POST("/prompts/delete", handleDeletePromptFile(dir))
}

// PromptMeta is the metadata extracted from a prompt .md file.
type PromptMeta struct {
	Name         string `json:"name"`
	Title        string `json:"title"`
	Description  string `json:"description,omitempty"`
	ArgumentHint string `json:"argumentHint,omitempty"`
}

// parsePromptMeta reads YAML frontmatter (description / argument-hint) from content.
func parsePromptMeta(name, content string) PromptMeta {
	title := strings.TrimSuffix(name, ".md")
	meta := PromptMeta{Name: name, Title: title}

	if !strings.HasPrefix(content, "---\n") {
		return meta
	}
	rest := content[4:]
	end := strings.Index(rest, "\n---")
	if end < 0 {
		return meta
	}
	for _, line := range strings.Split(rest[:end], "\n") {
		if after, ok := strings.CutPrefix(line, "description:"); ok {
			meta.Description = strings.Trim(strings.TrimSpace(after), `"'`)
		}
		if after, ok := strings.CutPrefix(line, "argument-hint:"); ok {
			meta.ArgumentHint = strings.Trim(strings.TrimSpace(after), `"'`)
		}
	}
	return meta
}

// safePromptName validates that the filename is a plain .md name with no path traversal.
func safePromptName(name string) bool {
	if name == "" {
		return false
	}
	if !strings.HasSuffix(name, ".md") {
		return false
	}
	// Reject anything that looks like a path
	if strings.ContainsAny(name, "/\\") || strings.Contains(name, "..") {
		return false
	}
	return true
}

func handleListPrompts(dir string) gin.HandlerFunc {
	return func(c *gin.Context) {
		entries, err := os.ReadDir(dir)
		if err != nil {
			if os.IsNotExist(err) {
				c.JSON(http.StatusOK, gin.H{"prompts": []any{}})
				return
			}
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		list := make([]PromptMeta, 0)
		for _, e := range entries {
			if e.IsDir() || !strings.HasSuffix(e.Name(), ".md") {
				continue
			}
			raw, _ := os.ReadFile(filepath.Join(dir, e.Name()))
			list = append(list, parsePromptMeta(e.Name(), string(raw)))
		}
		c.JSON(http.StatusOK, gin.H{"prompts": list})
	}
}

func handleGetPromptFile(dir string) gin.HandlerFunc {
	return func(c *gin.Context) {
		name := c.Query("name")
		if !safePromptName(name) {
			c.JSON(http.StatusBadRequest, gin.H{"error": "无效的文件名"})
			return
		}
		raw, err := os.ReadFile(filepath.Join(dir, name))
		if err != nil {
			c.JSON(http.StatusNotFound, gin.H{"error": "文件不存在"})
			return
		}
		c.JSON(http.StatusOK, gin.H{"content": string(raw)})
	}
}

func handleSavePromptFile(dir string) gin.HandlerFunc {
	return func(c *gin.Context) {
		var req struct {
			Name    string `json:"name"`
			Content string `json:"content"`
		}
		if err := c.ShouldBindJSON(&req); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}
		if !safePromptName(req.Name) {
			c.JSON(http.StatusBadRequest, gin.H{"error": "文件名必须以 .md 结尾，且不能含路径符"})
			return
		}
		if err := os.MkdirAll(dir, 0o755); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		if err := os.WriteFile(filepath.Join(dir, req.Name), []byte(req.Content), 0o644); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		c.JSON(http.StatusOK, gin.H{"ok": true})
	}
}

func handleDeletePromptFile(dir string) gin.HandlerFunc {
	return func(c *gin.Context) {
		var req struct {
			Name string `json:"name"`
		}
		if err := c.ShouldBindJSON(&req); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}
		if !safePromptName(req.Name) {
			c.JSON(http.StatusBadRequest, gin.H{"error": "无效的文件名"})
			return
		}
		if err := os.Remove(filepath.Join(dir, req.Name)); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		c.JSON(http.StatusOK, gin.H{"ok": true})
	}
}
