package dashboard

import (
	"errors"
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
	"go.yaml.in/yaml/v3"

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

func toServerResponse(server profile.AgentTeamServer) dto.ServerResponse {
	var cfg map[string]interface{}
	if len(server.Config) > 0 {
		_ = yaml.Unmarshal([]byte(server.Config), &cfg)
	}
	var agentName string
	if server.AgentID != "" {
		if a, ok := profile.Default().Agents().Get(server.AgentID); ok {
			agentName = a.Name
		}
	}
	return dto.ServerResponse{
		ID:          server.ID,
		Enabled:     server.Enabled,
		Name:        server.Name,
		Description: server.Description,
		AgentID:     server.AgentID,
		AgentName:   agentName,
		Config:      cfg,
	}
}

func serverRequestToProfile(req dto.ServerRequest) profile.AgentTeamServer {
	var cfgYAML []byte
	if len(req.Config) > 0 {
		cfgYAML, _ = yaml.Marshal(req.Config)
	}
	server := profile.AgentTeamServer{
		ID:      req.ID,
		Enabled: req.Enabled,
		Name:    req.Name,
		AgentID: req.AgentID,
		Config:  cfgYAML,
	}
	if req.Description != nil {
		server.Description = *req.Description
	}
	return server
}

// ---------------------------------------------------------------------------
// GET /servers
// ---------------------------------------------------------------------------

// ListServersHandler handles listing all AgentTeam server auth configurations.
type ListServersHandler struct{}

// NewListServersHandler creates a new ListServersHandler.
func NewListServersHandler() gin.HandlerFunc {
	return (&ListServersHandler{}).Handle
}

// Handle returns all AgentTeam server auth configurations.
func (h *ListServersHandler) Handle(c *gin.Context) {
	servers := profile.Default().Servers().All()
	resp := make([]dto.ServerResponse, 0, len(servers))
	for _, server := range servers {
		resp = append(resp, toServerResponse(server))
	}
	c.JSON(http.StatusOK, resp)
}

// ---------------------------------------------------------------------------
// POST /servers
// ---------------------------------------------------------------------------

// CreateServerHandler handles creating a new AgentTeam server auth configuration.
type CreateServerHandler struct {
	servers *profile.ServerManager
}

// NewCreateServerHandler creates a new CreateServerHandler.
func NewCreateServerHandler(servers *profile.ServerManager) gin.HandlerFunc {
	return (&CreateServerHandler{servers: servers}).Handle
}

// Handle creates a new AgentTeam server auth configuration from the request body.
func (h *CreateServerHandler) Handle(c *gin.Context) {
	var req dto.ServerRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("invalid request body: %v", err)})
		return
	}
	server := serverRequestToProfile(req)
	server.Enabled = true
	server.ID = ""
	if err := h.servers.Add(&server); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusCreated, toServerResponse(server))
}

// ---------------------------------------------------------------------------
// GET /servers/:id
// ---------------------------------------------------------------------------

// GetServerHandler handles retrieving a single AgentTeam server auth configuration by ID.
type GetServerHandler struct{}

// NewGetServerHandler creates a new GetServerHandler.
func NewGetServerHandler() gin.HandlerFunc {
	return (&GetServerHandler{}).Handle
}

// Handle returns a single AgentTeam server auth configuration.
func (h *GetServerHandler) Handle(c *gin.Context) {
	id := c.Param("id")
	server, ok := profile.Default().Servers().Get(id)
	if !ok {
		c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("server %q not found", id)})
		return
	}
	c.JSON(http.StatusOK, toServerResponse(server))
}

// ---------------------------------------------------------------------------
// PUT /servers/:id
// ---------------------------------------------------------------------------

// UpdateServerHandler handles updating a AgentTeam server auth configuration.
type UpdateServerHandler struct {
	servers *profile.ServerManager
}

// NewUpdateServerHandler creates a field-patch update handler. The mutation
// owner reads the current server under its per-server transaction lock.
func NewUpdateServerHandler(servers *profile.ServerManager) gin.HandlerFunc {
	return (&UpdateServerHandler{servers: servers}).Handle
}

// Handle updates a AgentTeam server auth configuration by ID.
func (h *UpdateServerHandler) Handle(c *gin.Context) {
	id := c.Param("id")
	var req dto.ServerRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("invalid request body: %v", err)})
		return
	}
	cfgYAML, _ := yaml.Marshal(req.Config)
	name := req.Name
	agentID := req.AgentID
	enabled := req.Enabled
	update := profile.ServerUpdate{
		Enabled:     &enabled,
		Name:        &name,
		Description: req.Description,
		AgentID:     &agentID,
	}
	if len(req.Config) > 0 {
		raw := types.RawMessage(cfgYAML)
		update.Config = &raw
	}
	server, err := h.servers.Patch(id, update)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, toServerResponse(server))
}

// NewRepairServerHandler creates the explicit runtime repair endpoint. It is
// intentionally separate from ordinary config updates because only a
// quarantined server may use it.
func NewRepairServerHandler(servers *profile.ServerManager) gin.HandlerFunc {
	return func(c *gin.Context) {
		id := c.Param("id")
		if _, ok := servers.Get(id); !ok {
			c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("server %q not found", id)})
			return
		}
		if err := servers.RepairServerRuntime(id); err != nil {
			status := http.StatusConflict
			if errors.Is(err, profile.ErrServerRuntimePreparing) {
				status = http.StatusServiceUnavailable
			}
			c.JSON(status, gin.H{"error": err.Error()})
			return
		}
		c.JSON(http.StatusOK, gin.H{"status": "repaired", "id": id})
	}
}

// ---------------------------------------------------------------------------
// DELETE /servers/:id
// ---------------------------------------------------------------------------

// DeleteServerHandler handles removing a AgentTeam server auth configuration.
type DeleteServerHandler struct {
	servers *profile.ServerManager
}

// NewDeleteServerHandler creates a new DeleteServerHandler.
func NewDeleteServerHandler(servers *profile.ServerManager) gin.HandlerFunc {
	return (&DeleteServerHandler{servers: servers}).Handle
}

// Handle deletes a AgentTeam server auth configuration by ID.
func (h *DeleteServerHandler) Handle(c *gin.Context) {
	id := c.Param("id")
	if _, ok := h.servers.Get(id); !ok {
		c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("server %q not found", id)})
		return
	}
	if err := h.servers.Delete(id); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"status": "deleted", "id": id})
}

// ---------------------------------------------------------------------------
// GET /servers/:id/tools
// ---------------------------------------------------------------------------

// ListServerToolsHandler returns the MCP tool definitions for the server runtime.
type ListServerToolsHandler struct {
	serverConnections types.ServerConnectionAccessor
	resolve           func(types.ServerConnection) []types.MCPToolDef
}

// NewListServerToolsHandler creates a new ListServerToolsHandler.
func NewListServerToolsHandler(
	serverConnections types.ServerConnectionAccessor,
	resolve func(types.ServerConnection) []types.MCPToolDef,
) gin.HandlerFunc {
	return (&ListServerToolsHandler{serverConnections: serverConnections, resolve: resolve}).Handle
}

// Handle returns the MCP tool names and descriptions for the specified server runtime.
func (h *ListServerToolsHandler) Handle(c *gin.Context) {
	id := c.Param("id")
	if h.serverConnections == nil {
		c.JSON(http.StatusOK, gin.H{"tools": []map[string]string{}})
		return
	}
	ch, ok := h.serverConnections.ServerConnection(id)
	if !ok {
		c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("server %q not found or not running", id)})
		return
	}
	var defs []types.MCPToolDef
	if h.resolve != nil {
		defs = h.resolve(ch)
	}
	tools := make([]map[string]string, 0, len(defs))
	for _, d := range defs {
		tools = append(tools, map[string]string{"name": d.Name, "description": d.Description})
	}
	c.JSON(http.StatusOK, gin.H{"tools": tools})
}
