package memory

import (
	"database/sql"
	"os"
	"path/filepath"
	"testing"
	"time"

	_ "modernc.org/sqlite"
)

func testDBPath(t *testing.T) string {
	dir := t.TempDir()
	return filepath.Join(dir, "test_memory.db")
}

func newTestStore(t *testing.T) *Store {
	t.Helper()
	dbPath := testDBPath(t)

	db, err := openDB(dbPath)
	if err != nil {
		t.Fatalf("open db: %v", err)
	}
	t.Cleanup(func() { db.Close() })

	s := &Store{db: db, path: dbPath}
	if err := s.migrate(); err != nil {
		t.Fatalf("migrate: %v", err)
	}
	return s
}

func openDB(path string) (*sql.DB, error) {
	if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
		return nil, err
	}
	return sql.Open("sqlite", path)
}

func TestStoreAndRetrieve(t *testing.T) {
	s := newTestStore(t)

	m := &Memory{
		Type:     TypeFact,
		Key:      "golang_version",
		Content:  "User uses Go 1.24",
		Source:   "conversation",
	}

	if err := s.Store(m); err != nil {
		t.Fatalf("store: %v", err)
	}

	if m.ID == "" {
		t.Fatal("expected ID to be set")
	}

	got, err := s.Get(m.ID)
	if err != nil {
		t.Fatalf("get: %v", err)
	}

	if got.Key != m.Key {
		t.Errorf("expected key %s, got %s", m.Key, got.Key)
	}
	if got.Content != m.Content {
		t.Errorf("expected content %s, got %s", m.Content, got.Content)
	}
}

func TestDelete(t *testing.T) {
	s := newTestStore(t)

	m := &Memory{
		Type:    TypePreference,
		Key:     "editor",
		Content: "vim",
	}
	s.Store(m)

	if err := s.Delete(m.ID); err != nil {
		t.Fatalf("delete: %v", err)
	}

	_, err := s.Get(m.ID)
	if err == nil {
		t.Error("expected error after delete")
	}
}

func TestList(t *testing.T) {
	s := newTestStore(t)

	for i := 0; i < 5; i++ {
		s.Store(&Memory{
			Type:    TypeFact,
			Key:     "fact_" + string(rune('a'+i)),
			Content: "content",
		})
	}

	memories, err := s.List(TypeFact, 10, 0)
	if err != nil {
		t.Fatalf("list: %v", err)
	}

	if len(memories) != 5 {
		t.Errorf("expected 5 memories, got %d", len(memories))
	}
}

func TestSearch(t *testing.T) {
	s := newTestStore(t)

	s.Store(&Memory{Type: TypeFact, Key: "language", Content: "Go is the primary language"})
	s.Store(&Memory{Type: TypeFact, Key: "editor", Content: "VSCode is the editor"})
	s.Store(&Memory{Type: TypeContext, Key: "project", Content: "Muyue is a Go project"})

	results, err := s.Search("Go language", 10)
	if err != nil {
		t.Fatalf("search: %v", err)
	}

	if len(results) == 0 {
		t.Error("expected search results")
	}
}

func TestRecallPreferences(t *testing.T) {
	s := newTestStore(t)

	s.Store(&Memory{Type: TypePreference, Key: "theme", Content: "dark"})
	s.Store(&Memory{Type: TypePreference, Key: "lang", Content: "fr"})
	s.Store(&Memory{Type: TypeFact, Key: "tool", Content: "go"})

	prefs, err := s.RecallPreferences()
	if err != nil {
		t.Fatalf("recall preferences: %v", err)
	}

	if len(prefs) != 2 {
		t.Errorf("expected 2 preferences, got %d", len(prefs))
	}
}

func TestRecallRecent(t *testing.T) {
	s := newTestStore(t)

	s.Store(&Memory{Type: TypeFact, Key: "old", Content: "old fact"})

	recent, err := s.RecallRecent(time.Now().Add(-1*time.Hour), 10)
	if err != nil {
		t.Fatalf("recall recent: %v", err)
	}

	if len(recent) == 0 {
		t.Error("expected recent memories")
	}
}

func TestStorePreference(t *testing.T) {
	s := newTestStore(t)

	if err := s.StorePreference("editor", "vim"); err != nil {
		t.Fatalf("store preference: %v", err)
	}

	prefs, _ := s.RecallPreferences()
	if len(prefs) != 1 {
		t.Errorf("expected 1 preference, got %d", len(prefs))
	}
}

func TestCount(t *testing.T) {
	s := newTestStore(t)

	s.Store(&Memory{Type: TypeFact, Key: "a", Content: "a"})
	s.Store(&Memory{Type: TypeFact, Key: "b", Content: "b"})

	count, err := s.Count()
	if err != nil {
		t.Fatalf("count: %v", err)
	}
	if count != 2 {
		t.Errorf("expected 2, got %d", count)
	}
}
