package dashboard

import (
	"fmt"
	"net/http"
	"strings"
	"unicode/utf8"

	"github.com/gin-gonic/gin"

	"github.com/tidbcloud/AgentTeam/desktop/internal/agent"
	"github.com/tidbcloud/AgentTeam/desktop/internal/api/dto"
	"github.com/tidbcloud/AgentTeam/desktop/internal/profile"
	"github.com/tidbcloud/AgentTeam/desktop/internal/promptcompiler"
)

// maxAgentDocBytes caps the size of an identity document write.
const maxAgentDocBytes = promptcompiler.DefaultMaxWorkspaceDocBytes
const maxAgentDocCharacters = promptcompiler.DefaultMaxIdentityDocCharacters

// ---------------------------------------------------------------------------
// GET /agents/:id/docs/:kind
// ---------------------------------------------------------------------------

// GetAgentDocHandler reads an identity document from an agent workspace.
type GetAgentDocHandler struct{}

// NewGetAgentDocHandler creates a new GetAgentDocHandler.
func NewGetAgentDocHandler() gin.HandlerFunc {
	return (&GetAgentDocHandler{}).Handle
}

// Handle returns the requested doc content.
func (h *GetAgentDocHandler) Handle(c *gin.Context) {
	id := c.Param("id")
	if _, ok := profile.Default().Agents().Get(id); !ok {
		c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("agent %q not found", id)})
		return
	}
	kind, err := agent.ParseDocKind(c.Param("kind"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	content, err := agent.ReadDoc(profile.AgentWorkDir(id), kind)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, dto.AgentDocResponse{Kind: string(kind), Content: content})
}

// ---------------------------------------------------------------------------
// PUT /agents/:id/docs/:kind
// ---------------------------------------------------------------------------

// UpdateAgentDocHandler writes an identity document into an agent workspace.
type UpdateAgentDocHandler struct{}

// NewUpdateAgentDocHandler creates a new UpdateAgentDocHandler.
func NewUpdateAgentDocHandler() gin.HandlerFunc {
	return (&UpdateAgentDocHandler{}).Handle
}

// Handle persists the doc content.
func (h *UpdateAgentDocHandler) Handle(c *gin.Context) {
	id := c.Param("id")
	if _, ok := profile.Default().Agents().Get(id); !ok {
		c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("agent %q not found", id)})
		return
	}
	kind, err := agent.ParseDocKind(c.Param("kind"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	var req dto.AgentDocRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("invalid request body: %v", err)})
		return
	}
	if kind == agent.DocKindIdentity {
		characters := utf8.RuneCountInString(strings.TrimSpace(req.Content))
		if characters > maxAgentDocCharacters {
			c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("identity document exceeds %d characters (got %d); shorten the document", maxAgentDocCharacters, characters)})
			return
		}
	}
	if len(req.Content) > maxAgentDocBytes {
		c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("content exceeds %d bytes", maxAgentDocBytes)})
		return
	}
	workDir := profile.AgentWorkDir(id)
	if err := agent.WriteDoc(workDir, kind, req.Content); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, dto.AgentDocResponse{Kind: string(kind), Content: req.Content})
}
