package memory

import (
	"time"

	"github.com/vllm-project/semantic-router/src/semantic-router/pkg/config"
)

// MemoryType represents the category of a memory in the agentic memory system.
type MemoryType string

const (
	// MemoryTypeSemantic represents facts, preferences, knowledge.
	// Example: "User's budget for Hawaii is $10,000"
	MemoryTypeSemantic MemoryType = "semantic"

	// MemoryTypeProcedural represents instructions, how-to, steps.
	// Example: "To deploy payment-service: run npm build, then docker push"
	MemoryTypeProcedural MemoryType = "procedural"

	// MemoryTypeEpisodic represents session summaries, past events.
	// Example: "On Dec 29 2024, user planned Hawaii vacation with $10K budget"
	MemoryTypeEpisodic MemoryType = "episodic"
)

// Memory represents a stored memory unit in the agentic memory system
type Memory struct {
	// ID is the unique identifier for this memory
	ID string `json:"id"`

	// Type is the category of this memory (semantic, procedural, episodic)
	Type MemoryType `json:"type"`

	// Content is the actual memory text
	// Should be self-contained with context (e.g., "budget for Hawaii is $10K" not just "$10K")
	Content string `json:"content"`

	// Embedding is the vector representation (not serialized to JSON)
	Embedding []float32 `json:"-"`

	// UserID is the owner of this memory (for user isolation)
	UserID string `json:"user_id"`

	// ProjectID is an optional project scope
	ProjectID string `json:"project_id,omitempty"`

	// Source indicates where this memory came from
	Source string `json:"source,omitempty"`

	// CreatedAt is when the memory was first stored
	CreatedAt time.Time `json:"created_at"`

	// UpdatedAt is when the memory was last modified
	UpdatedAt time.Time `json:"updated_at,omitempty"`

	// AccessCount tracks how often this memory is retrieved (used for retention score S = S0 + AccessCount)
	AccessCount int `json:"access_count"`

	// LastAccessed is when the memory was last retrieved (used for retention score t and reinforcement)
	LastAccessed time.Time `json:"last_accessed,omitempty"`

	// Importance is a score for prioritizing memories (0.0 to 1.0)
	Importance float32 `json:"importance"`

	// --- Provenance fields ---

	// CreatedByUserID is the user whose conversation produced this memory.
	CreatedByUserID string `json:"created_by_user_id,omitempty"`

	// ConversationID is the session that produced this memory.
	ConversationID string `json:"conversation_id,omitempty"`

	// CreatedVia records the creation method: "llm_extraction", "api", "import".
	CreatedVia string `json:"created_via,omitempty"`
}

// RetrieveResult represents a memory retrieved from search with its relevance score
type RetrieveResult struct {
	// Memory is the retrieved memory
	Memory *Memory `json:"memory"`

	// Score is the similarity score (0.0 to 1.0, higher = more relevant)
	Score float32 `json:"score"`
}

// RetrieveOptions configures memory retrieval
type RetrieveOptions struct {
	// Query is the search query (will be embedded for vector search)
	Query string

	// UserID filters memories to this user only
	UserID string

	// ProjectID optionally filters to a specific project
	ProjectID string

	// Types optionally filters to specific memory types
	Types []MemoryType

	// Limit is the maximum number of results to return (default: 5)
	Limit int

	// Threshold is the minimum similarity score (range 0.0 to 1.0, default: 0.70)
	Threshold float32

	// HybridSearch enables BM25 + n-gram re-ranking on top of vector search
	HybridSearch bool

	// HybridMode selects the score fusion method: "weighted" (default) or "rrf"
	HybridMode string

	// AdaptiveThreshold enables cluster-based adaptive thresholding.
	// When true, the retriever detects the largest score gap among candidates
	// and only returns those above the gap, subject to the base Threshold
	// as a floor. This avoids injecting weakly-relevant memories.
	AdaptiveThreshold bool
}

// DefaultMemoryConfig returns a default memory configuration.
// EmbeddingModel is intentionally omitted - let router auto-detect from embedding_models config.
func DefaultMemoryConfig() config.MemoryConfig {
	return config.MemoryConfig{
		Milvus: config.MemoryMilvusConfig{
			Dimension: 384, // Safe default, will be overridden by router
		},
		DefaultRetrievalLimit:      5,
		DefaultSimilarityThreshold: 0.70,
	}
}

// ListOptions configures memory listing (non-semantic, filter-based retrieval)
type ListOptions struct {
	// UserID filters memories to this user only (required)
	UserID string

	// Types optionally filters to specific memory types
	Types []MemoryType

	// Limit is the maximum number of results to return (default: 20, max: 100)
	Limit int
}

// ListResult contains the memories returned by a List operation
type ListResult struct {
	// Memories is the list of memories returned
	Memories []*Memory `json:"memories"`

	// Total is the total number of matching memories
	Total int `json:"total"`

	// Limit is the limit that was applied
	Limit int `json:"limit"`
}

// MemoryFilter is the interface for post-retrieval, pre-injection memory
// validation. Implementations score, trim, and filter retrieved memories
// before they are injected into the LLM request context.
//
// Returning nil or an empty slice signals that no memories should be injected.
// Implementations must be safe for concurrent use.
type MemoryFilter interface {
	Filter(memories []*RetrieveResult) []*RetrieveResult
}

// MemoryFilterFactory constructs a MemoryFilter from global and optional
// per-decision configuration. Returning nil means filtering is disabled
// (equivalent to NoopFilter).
type MemoryFilterFactory func(global config.MemoryReflectionConfig, perDecision *config.MemoryReflectionConfig) MemoryFilter

// MemoryScope defines the scope for bulk operations (e.g., ForgetByScope)
type MemoryScope struct {
	// UserID is required - all operations are user-scoped
	UserID string

	// ProjectID optionally narrows scope to a project
	ProjectID string

	// Types optionally narrows scope to specific memory types
	Types []MemoryType
}
