package dialogue

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

	"github.com/kami/maven/internal/store"
)

// openStore — a store on disk, so a second handle can reopen the same file.
func openStore(t *testing.T, path string) *store.Store {
	t.Helper()
	s, err := store.Open(context.Background(), path)
	if err != nil {
		t.Fatalf("open store: %v", err)
	}
	t.Cleanup(func() { _ = s.Close() })
	return s
}

// A session written before a restart comes back and still merges a follow-up.
func TestSessionSurvivesRestart(t *testing.T) {
	ctx := context.Background()
	path := filepath.Join(t.TempDir(), "maven_test.db")
	now := time.Now().UTC().Truncate(time.Millisecond)

	first := openStore(t, path)
	before := NewPersistentSessionStore(2*time.Minute, first)
	before.Put("voice", &Session{
		Intent:    IntentReminder,
		Slots:     Slots{Text: "полить цветы", Time: now.Add(time.Hour), HasTime: true},
		Timestamp: now,
		TTL:       2 * time.Minute,
	})
	if err := first.Close(); err != nil {
		t.Fatalf("close: %v", err)
	}

	// fresh handle, fresh in-memory map — as after a daemon restart
	after := openStore(t, path)
	reloaded := NewPersistentSessionStore(2*time.Minute, after)
	if err := reloaded.Load(ctx, now.Add(10*time.Second)); err != nil {
		t.Fatalf("Load: %v", err)
	}
	sess := reloaded.Get("voice", now.Add(10*time.Second))
	if sess == nil {
		t.Fatal("session did not survive the restart")
	}
	if sess.Intent != IntentReminder {
		t.Fatalf("intent = %q, want reminder", sess.Intent)
	}
	// the follow-up carries no text of its own; it must inherit the old one
	merged := InheritSlots(sess.Slots, Slots{Time: now.Add(2 * time.Hour), HasTime: true})
	if merged.Text != "полить цветы" {
		t.Fatalf("merged text = %q, want the earlier turn's text", merged.Text)
	}
	if !merged.Time.Equal(now.Add(2 * time.Hour)) {
		t.Fatalf("merged time = %v, want the follow-up's time", merged.Time)
	}
}

// A session past its TTL is dead: a restart must not bring it back.
func TestExpiredSessionNotResurrected(t *testing.T) {
	ctx := context.Background()
	path := filepath.Join(t.TempDir(), "maven_test.db")
	now := time.Now().UTC().Truncate(time.Millisecond)

	first := openStore(t, path)
	before := NewPersistentSessionStore(2*time.Minute, first)
	before.Put("voice", &Session{
		Intent:    IntentReminder,
		Slots:     Slots{Text: "полить цветы"},
		Timestamp: now,
		TTL:       time.Minute,
	})
	if err := first.Close(); err != nil {
		t.Fatalf("close: %v", err)
	}

	after := openStore(t, path)
	reloaded := NewPersistentSessionStore(2*time.Minute, after)
	later := now.Add(5 * time.Minute) // well past the 1-min TTL
	if err := reloaded.Load(ctx, later); err != nil {
		t.Fatalf("Load: %v", err)
	}
	if sess := reloaded.Get("voice", later); sess != nil {
		t.Fatalf("expired session came back: %+v", sess)
	}
	// and it is gone from the DB too, not just from memory
	rows, err := after.LoadDialogueSessions(ctx, later)
	if err != nil {
		t.Fatalf("LoadDialogueSessions: %v", err)
	}
	if len(rows) != 0 {
		t.Fatalf("expired row still in the DB: %+v", rows)
	}
}

// Delete removes the row as well, so an ended conversation stays ended.
func TestDeleteRemovesPersistedSession(t *testing.T) {
	ctx := context.Background()
	path := filepath.Join(t.TempDir(), "maven_test.db")
	now := time.Now().UTC().Truncate(time.Millisecond)

	s := openStore(t, path)
	ss := NewPersistentSessionStore(2*time.Minute, s)
	ss.Put("voice", &Session{Intent: IntentChat, Timestamp: now, TTL: time.Minute})
	ss.Delete("voice")
	rows, err := s.LoadDialogueSessions(ctx, now)
	if err != nil {
		t.Fatalf("LoadDialogueSessions: %v", err)
	}
	if len(rows) != 0 {
		t.Fatalf("row survived Delete: %+v", rows)
	}
}
