package workspace

import (
	"context"
	"path/filepath"
	"time"

	"github.com/diillson/chatcli/cli/workspace/memory"
	"github.com/diillson/chatcli/cli/workspace/threatscan"
	"go.uber.org/zap"
)

// DailyNote represents a single day's note.
type DailyNote struct {
	Date    time.Time
	Path    string
	Content string
}

// MemoryStore is the backward-compatible facade that delegates to the
// new structured memory.Manager. All existing callers (ContextBuilder,
// memoryWorker, memory_command) continue to work through this interface.
type MemoryStore struct {
	manager   *memory.Manager
	memoryDir string
	logger    *zap.Logger
}

// NewMemoryStore creates a new memory store backed by the structured Manager.
func NewMemoryStore(baseDir string, logger *zap.Logger) *MemoryStore {
	memDir := filepath.Join(baseDir, "memory")
	// ConfigFromEnv (not DefaultConfig) so the pre-existing CHATCLI_MEMORY_*
	// overrides actually take effect on the structured-memory path.
	mgr := memory.NewManager(memDir, memory.ConfigFromEnv(), logger)

	return &MemoryStore{
		manager:   mgr,
		memoryDir: memDir,
		logger:    logger,
	}
}

// Manager returns the underlying memory.Manager for advanced operations.
func (ms *MemoryStore) Manager() *memory.Manager {
	return ms.manager
}

// SetWorkspaceDir sets the current session's workspace directory so that
// new facts are annotated with the project they were learned in.
func (ms *MemoryStore) SetWorkspaceDir(dir string) {
	ms.manager.SetWorkspaceDir(dir)
}

// EnsureDirectories creates the memory directory structure.
func (ms *MemoryStore) EnsureDirectories() error {
	return ms.manager.EnsureDirectories()
}

// --- Long-term Memory ---

// ReadLongTerm returns the rendered long-term memory content.
func (ms *MemoryStore) ReadLongTerm() string {
	return ms.manager.ReadLongTerm()
}

// WriteLongTerm replaces all long-term memory.
func (ms *MemoryStore) WriteLongTerm(content string) error {
	return ms.manager.WriteLongTerm(content)
}

// AppendLongTerm adds new content to long-term memory.
func (ms *MemoryStore) AppendLongTerm(entry string) error {
	return ms.manager.AppendLongTerm(entry)
}

// --- Daily Notes ---

// TodayNotePath returns the path for today's note.
func (ms *MemoryStore) TodayNotePath() string {
	return ms.manager.TodayNotePath()
}

// WriteDailyNote appends to today's daily note.
func (ms *MemoryStore) WriteDailyNote(entry string) error {
	return ms.manager.WriteDailyNote(entry)
}

// GetRecentDailyNotes returns the last N days of notes.
func (ms *MemoryStore) GetRecentDailyNotes(days int) []DailyNote {
	managerNotes := ms.manager.GetRecentDailyNotes(days)
	notes := make([]DailyNote, len(managerNotes))
	for i, n := range managerNotes {
		notes[i] = DailyNote{
			Date:    n.Date,
			Path:    n.Path,
			Content: n.Content,
		}
	}
	return notes
}

// GetMemoryContext builds the memory section for the system prompt.
// Uses smart retrieval when no hints are provided.
func (ms *MemoryStore) GetMemoryContext() string {
	return ms.sanitizeForPrompt(ms.manager.GetMemoryContext())
}

// GetRelevantContext returns memory tailored to conversation hints.
func (ms *MemoryStore) GetRelevantContext(hints []string) string {
	return ms.sanitizeForPrompt(ms.manager.GetRelevantContext(hints))
}

// GetMemoryIndex returns the compact, stable memory digest. See
// memory.Manager.GetMemoryIndex.
func (ms *MemoryStore) GetMemoryIndex(budget int) string {
	return ms.sanitizeForPrompt(ms.manager.GetMemoryIndex(budget))
}

// GetRelevantContextWithHyDE delegates to the manager's HyDE-aware
// retrieval. See memory.Manager.GetRelevantContextWithHyDE.
func (ms *MemoryStore) GetRelevantContextWithHyDE(ctx context.Context, query string, hints []string, augmenter *memory.HyDEAugmenter) string {
	return ms.sanitizeForPrompt(ms.manager.GetRelevantContextWithHyDE(ctx, query, hints, augmenter))
}

// sanitizeForPrompt neutralizes prompt-injection / exec / persistence
// payloads in memory before it is injected into the system prompt. Memory
// is auto-curated and should never carry shell scripts, so it uses the
// stricter ScopeMemory. The on-disk store is never modified.
func (ms *MemoryStore) sanitizeForPrompt(s string) string {
	if s == "" || !threatscan.Enabled() {
		return s
	}
	sanitized, blocked := threatscan.Sanitize(s, threatscan.ScopeMemory)
	if blocked > 0 && ms.logger != nil {
		ms.logger.Warn("threatscan: neutralized lines in memory context",
			zap.Int("blocked", blocked))
	}
	return sanitized
}

// AttachVectorIndex attaches a vector index used by HyDE Phase 3b.
// Pass nil to detach.
func (ms *MemoryStore) AttachVectorIndex(v *memory.VectorIndex) {
	ms.manager.AttachVectorIndex(v)
}

// VectorIndex returns the attached vector index (may be nil).
func (ms *MemoryStore) VectorIndex() *memory.VectorIndex {
	return ms.manager.VectorIndex()
}

// ProcessExtraction processes enhanced extraction output from the memory
// worker. Void for API compatibility; use ProcessExtractionResult for the
// summary.
func (ms *MemoryStore) ProcessExtraction(response string) {
	ms.manager.ProcessExtraction(response)
}

// ProcessExtractionResult is ProcessExtraction with a summary of what was
// persisted.
func (ms *MemoryStore) ProcessExtractionResult(response string) memory.ExtractionSummary {
	return ms.manager.ProcessExtractionResult(response)
}

// RememberFact stores a single fact deterministically (no LLM). category
// may be empty to auto-classify. Returns true if newly added.
func (ms *MemoryStore) RememberFact(content, category string) bool {
	return ms.manager.RememberFact(content, category)
}

// UpdateProfile applies deterministic profile updates and returns whether
// anything changed.
func (ms *MemoryStore) UpdateProfile(updates map[string]string) bool {
	return ms.manager.UpdateProfile(updates)
}

// ForgetFacts removes facts matching substr and returns how many were removed.
func (ms *MemoryStore) ForgetFacts(substr string) int {
	return ms.manager.ForgetFacts(substr)
}

// RecordInteraction records a usage event.
func (ms *MemoryStore) RecordInteraction(event memory.InteractionEvent) {
	ms.manager.RecordInteraction(event)
}
