package session

import (
	"context"
	"net/http"
	"strings"
	"time"

	"github.com/gin-gonic/gin"

	"github.com/tidbcloud/AgentTeam/desktop/internal/memorywatcher"
)

// MemorySyncStore exposes daemon-local Memory sync state and retry. The state
// is transient runtime data; implementations must not persist it to TiDB.
type MemorySyncStore interface {
	MemorySyncStatus(agentID string) (memorywatcher.Status, bool)
	RetryMemorySync(ctx context.Context, agentID string)
}

type memorySyncStatusResponse struct {
	State   memorywatcher.StatusState  `json:"state"`
	Reason  memorywatcher.StatusReason `json:"reason,omitempty"`
	RetryAt time.Time                  `json:"retryAt,omitzero"`
}

// NewMemorySyncStatusHandler returns the current low-cardinality runtime state.
func NewMemorySyncStatusHandler(store MemorySyncStore) gin.HandlerFunc {
	return func(c *gin.Context) {
		agentID, ok := agentIDParam(c)
		if !ok {
			return
		}
		if store == nil {
			c.JSON(http.StatusServiceUnavailable, gin.H{"error": "memory sync status unavailable"})
			return
		}
		status, ok := store.MemorySyncStatus(agentID)
		if !ok {
			c.JSON(http.StatusNotFound, gin.H{"error": "memory sync status unavailable"})
			return
		}
		c.JSON(http.StatusOK, memorySyncStatusResponse{
			State:   status.State,
			Reason:  status.Reason,
			RetryAt: status.RetryAt,
		})
	}
}

// NewMemorySyncRetryHandler requests retry through the watcher's serialized
// push pipeline or, when no watcher exists, the normal access refresh path.
func NewMemorySyncRetryHandler(store MemorySyncStore) gin.HandlerFunc {
	return func(c *gin.Context) {
		agentID, ok := agentIDParam(c)
		if !ok {
			return
		}
		if store == nil {
			c.JSON(http.StatusServiceUnavailable, gin.H{"error": "memory sync retry unavailable"})
			return
		}
		if _, ok := store.MemorySyncStatus(agentID); !ok {
			c.JSON(http.StatusNotFound, gin.H{"error": "memory sync status unavailable"})
			return
		}
		store.RetryMemorySync(c.Request.Context(), agentID)
		c.JSON(http.StatusAccepted, gin.H{"retryRequested": true})
	}
}

func agentIDParam(c *gin.Context) (string, bool) {
	agentID := strings.TrimSpace(c.Param("agentId"))
	if agentID == "" {
		agentID = strings.TrimSpace(c.Param("id"))
	}
	if agentID == "" || strings.ContainsAny(agentID, `/\`) {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid agent id"})
		return "", false
	}
	return agentID, true
}
