package handlers

import (
	"net/http"
	"os"
	"runtime"

	"github.com/gin-gonic/gin"
	"sproutclaw-web/internal/config"
	"sproutclaw-web/internal/db"
	"sproutclaw-web/internal/models"
	"sproutclaw-web/internal/rpc"
	"sproutclaw-web/internal/services"
)

// RegisterSettings registers all settings-related routes.
func RegisterSettings(r *gin.RouterGroup, cfg *config.Config, database *db.DB, piClient *rpc.Client) {
	r.GET("/settings", handleGetSettings(cfg, database))
	r.POST("/settings/skills/toggle", handleToggleSkill(cfg, piClient))
	r.POST("/settings/extensions/toggle", handleToggleExtension(cfg, piClient))
	r.POST("/settings/mcp/server/toggle", handleToggleMCPServer(cfg, piClient))
	r.POST("/settings/mcp/tool/toggle", handleToggleMCPTool(cfg, piClient))
	r.POST("/settings/reload", handleReload(piClient))
	r.POST("/settings/models-config", handleSetModelsConfig(cfg, piClient))
	r.POST("/settings/system-prompt", handleSetSystemPrompt(cfg, piClient))
	r.POST("/settings/avatars", handleSetAvatars(database))
	r.GET("/environment", handleEnvironment(cfg))
}

func handleGetSettings(cfg *config.Config, database *db.DB) gin.HandlerFunc {
	return func(c *gin.Context) {
		systemPrompt, _ := services.ReadSystemPrompt(cfg.SystemPromptFile)
		modelsConfig, _ := services.ReadModelsConfig(cfg.ModelsConfigFile)
		userAvatar, _ := database.GetConfig("userAvatarUrl")
		agentAvatar, _ := database.GetConfig("agentAvatarUrl")

		c.JSON(http.StatusOK, gin.H{
			"systemPrompt":     systemPrompt,
			"systemPromptPath": cfg.SystemPromptFile,
			"modelsConfig":     modelsConfig,
			"modelsConfigPath": cfg.ModelsConfigFile,
			"userAvatarUrl":    userAvatar,
			"agentAvatarUrl":   agentAvatar,
			"skills":           services.ListSkills(cfg.AgentDir),
			"extensions":       services.ListExtensions(cfg.AgentDir),
			"extensionsPath":   cfg.ExtensionsDir,
			"mcpTools":         services.ReadMCPServers(cfg.McpConfigFile, cfg.McpCacheFile),
			"mcpConfigPath":    cfg.McpConfigFile,
			"mcpCachePath":     cfg.McpCacheFile,
		})
	}
}

func handleToggleSkill(cfg *config.Config, piClient *rpc.Client) gin.HandlerFunc {
	return func(c *gin.Context) {
		var req models.SkillToggleRequest
		if err := c.ShouldBindJSON(&req); err != nil {
			c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: err.Error()})
			return
		}
		if req.Path == "" {
			c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: "skill path 无效"})
			return
		}
		if err := services.ToggleSkill(cfg.AgentDir, req.Path, req.Enabled); err != nil {
			c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: err.Error()})
			return
		}
		reloadAgent(piClient)
		c.JSON(http.StatusOK, gin.H{"ok": true})
	}
}

func handleToggleExtension(cfg *config.Config, piClient *rpc.Client) gin.HandlerFunc {
	return func(c *gin.Context) {
		var req models.ExtensionToggleRequest
		if err := c.ShouldBindJSON(&req); err != nil {
			c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: err.Error()})
			return
		}
		if req.Path == "" {
			c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: "extension path 无效"})
			return
		}
		if err := services.ToggleExtension(cfg.AgentDir, req.Path, req.Enabled); err != nil {
			c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: err.Error()})
			return
		}
		reloadAgent(piClient)
		c.JSON(http.StatusOK, gin.H{"ok": true})
	}
}

func handleToggleMCPServer(cfg *config.Config, piClient *rpc.Client) gin.HandlerFunc {
	return func(c *gin.Context) {
		var req models.MCPServerToggleRequest
		if err := c.ShouldBindJSON(&req); err != nil {
			c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: err.Error()})
			return
		}
		if req.Server == "" {
			c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: "server 名称无效"})
			return
		}
		if err := services.ToggleMCPServer(cfg.McpConfigFile, req.Server, req.Enabled); err != nil {
			c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: err.Error()})
			return
		}
		reloadAgent(piClient)
		c.JSON(http.StatusOK, gin.H{"ok": true})
	}
}

func handleToggleMCPTool(cfg *config.Config, piClient *rpc.Client) gin.HandlerFunc {
	return func(c *gin.Context) {
		var req models.MCPToolToggleRequest
		if err := c.ShouldBindJSON(&req); err != nil {
			c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: err.Error()})
			return
		}
		if req.Server == "" || req.Tool == "" {
			c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: "server / tool 名称无效"})
			return
		}
		if err := services.ToggleMCPTool(cfg.McpConfigFile, req.Server, req.Tool, req.Enabled); err != nil {
			c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: err.Error()})
			return
		}
		reloadAgent(piClient)
		c.JSON(http.StatusOK, gin.H{"ok": true})
	}
}

func handleReload(piClient *rpc.Client) gin.HandlerFunc {
	return func(c *gin.Context) {
		reloadAgent(piClient)
		c.JSON(http.StatusOK, gin.H{"ok": true})
	}
}

func handleSetModelsConfig(cfg *config.Config, piClient *rpc.Client) gin.HandlerFunc {
	return func(c *gin.Context) {
		var req models.ModelsConfigRequest
		if err := c.ShouldBindJSON(&req); err != nil {
			c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: err.Error()})
			return
		}
		// Guard: the field must be present (not just empty) to avoid clobbering.
		if req.ModelsConfig == nil {
			c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: "缺少 modelsConfig 字段"})
			return
		}
		if err := services.WriteModelsConfig(cfg.ModelsConfigFile, *req.ModelsConfig); err != nil {
			c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: err.Error()})
			return
		}
		reloadAgent(piClient)
		c.JSON(http.StatusOK, gin.H{"ok": true, "modelsConfigPath": cfg.ModelsConfigFile})
	}
}

func handleSetSystemPrompt(cfg *config.Config, piClient *rpc.Client) gin.HandlerFunc {
	return func(c *gin.Context) {
		var req models.SystemPromptRequest
		if err := c.ShouldBindJSON(&req); err != nil {
			c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: err.Error()})
			return
		}
		// Guard: the field must be present. A missing field would otherwise
		// silently write an empty string and wipe the prompt file.
		if req.SystemPrompt == nil {
			c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: "缺少 systemPrompt 字段"})
			return
		}
		if err := services.WriteSystemPrompt(cfg.SystemPromptFile, *req.SystemPrompt); err != nil {
			c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: err.Error()})
			return
		}
		reloadAgent(piClient)
		c.JSON(http.StatusOK, gin.H{"ok": true, "systemPromptPath": cfg.SystemPromptFile})
	}
}

func handleSetAvatars(database *db.DB) gin.HandlerFunc {
	return func(c *gin.Context) {
		var req models.AvatarsSetRequest
		if err := c.ShouldBindJSON(&req); err != nil {
			c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: err.Error()})
			return
		}
		userURL := normalizeAvatarURL(req.UserAvatarURL)
		agentURL := normalizeAvatarURL(req.AgentAvatarURL)
		_ = database.SetConfig("userAvatarUrl", userURL)
		_ = database.SetConfig("agentAvatarUrl", agentURL)
		c.JSON(http.StatusOK, gin.H{"ok": true, "userAvatarUrl": userURL, "agentAvatarUrl": agentURL})
	}
}

func handleEnvironment(cfg *config.Config) gin.HandlerFunc {
	return func(c *gin.Context) {
		hostname, _ := os.Hostname()
		cwd, _ := os.Getwd()
		c.JSON(http.StatusOK, gin.H{
			"nodeVersion": runtime.Version(),
			"platform":    runtime.GOOS,
			"arch":        runtime.GOARCH,
			"hostname":    hostname,
			"pid":         os.Getpid(),
			"cwd":         cwd,
			"port":        cfg.Port,
			"repoRoot":    cfg.RepoRoot,
		})
	}
}

func reloadAgent(piClient *rpc.Client) {
	_, _ = piClient.SendCmd(models.RPCCommand{Type: "reload"})
}
