package store_test

import (
	"context"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestSetActionThenActionsFor(t *testing.T) {
	ctx := context.Background()
	s := newStore(t)
	resetDB(t, rawPool(t))

	require.NoError(t, s.SetAction(ctx, userA, videoX, "watched"))

	got, err := s.ActionsFor(ctx, userA, []string{videoX})
	require.NoError(t, err)
	require.Equal(t, map[string][]string{videoX: {"watched"}}, got)
}

func TestSetActionWatchedSkippedMutuallyExclusive(t *testing.T) {
	ctx := context.Background()
	s := newStore(t)
	resetDB(t, rawPool(t))

	require.NoError(t, s.SetAction(ctx, userA, videoX, "watched"))
	// Switching to skipped must clear watched (and vice versa).
	require.NoError(t, s.SetAction(ctx, userA, videoX, "skipped"))

	got, err := s.ActionsFor(ctx, userA, []string{videoX})
	require.NoError(t, err)
	require.Equal(t, map[string][]string{videoX: {"skipped"}}, got, "skipped replaces watched")

	require.NoError(t, s.SetAction(ctx, userA, videoX, "watched"))
	got, err = s.ActionsFor(ctx, userA, []string{videoX})
	require.NoError(t, err)
	require.Equal(t, map[string][]string{videoX: {"watched"}}, got, "watched replaces skipped")
}

func TestSetActionSavedCoexists(t *testing.T) {
	ctx := context.Background()
	s := newStore(t)
	resetDB(t, rawPool(t))

	require.NoError(t, s.SetAction(ctx, userA, videoX, "watched"))
	require.NoError(t, s.SetAction(ctx, userA, videoX, "saved"))

	got, err := s.ActionsFor(ctx, userA, []string{videoX})
	require.NoError(t, err)
	// ActionsFor orders by action: saved, watched.
	require.Equal(t, map[string][]string{videoX: {"saved", "watched"}}, got,
		"saved is independent and coexists with watched")
}

func TestClearAction(t *testing.T) {
	ctx := context.Background()
	s := newStore(t)
	resetDB(t, rawPool(t))

	require.NoError(t, s.SetAction(ctx, userA, videoX, "saved"))
	require.NoError(t, s.ClearAction(ctx, userA, videoX, "saved"))

	got, err := s.ActionsFor(ctx, userA, []string{videoX})
	require.NoError(t, err)
	require.Empty(t, got, "cleared action no longer active")

	// Clearing an action that was never set is a harmless no-op.
	require.NoError(t, s.ClearAction(ctx, userA, videoX, "watched"))
}

func TestSetActionIsIdempotent(t *testing.T) {
	ctx := context.Background()
	s := newStore(t)
	p := rawPool(t)
	resetDB(t, p)

	require.NoError(t, s.SetAction(ctx, userA, videoX, "watched"))
	require.NoError(t, s.SetAction(ctx, userA, videoX, "watched"))

	var count int
	require.NoError(t, p.QueryRow(ctx,
		`SELECT count(*) FROM summary_actions
		  WHERE user_id = $1 AND video_id = $2 AND action = 'watched'`,
		userA, videoX).Scan(&count))
	require.Equal(t, 1, count, "re-setting must refresh, not duplicate")
}

func TestSetActionRejectsInvalid(t *testing.T) {
	ctx := context.Background()
	s := newStore(t)
	resetDB(t, rawPool(t))

	require.Error(t, s.SetAction(ctx, userA, videoX, "bookmarked"))
	require.Error(t, s.ClearAction(ctx, userA, videoX, ""))
}

func TestActionsAreUserScoped(t *testing.T) {
	ctx := context.Background()
	s := newStore(t)
	resetDB(t, rawPool(t))

	require.NoError(t, s.SetAction(ctx, userA, videoX, "watched"))

	got, err := s.ActionsFor(ctx, userB, []string{videoX})
	require.NoError(t, err)
	require.Empty(t, got, "user B must not see user A's actions")
}

func TestActionsForMultipleVideos(t *testing.T) {
	ctx := context.Background()
	s := newStore(t)
	resetDB(t, rawPool(t))

	require.NoError(t, s.SetAction(ctx, userA, videoX, "watched"))
	require.NoError(t, s.SetAction(ctx, userA, videoY, "saved"))

	got, err := s.ActionsFor(ctx, userA, []string{videoX, videoY})
	require.NoError(t, err)
	require.Equal(t, map[string][]string{
		videoX: {"watched"},
		videoY: {"saved"},
	}, got)

	// Empty input -> empty (non-nil) map, no query.
	empty, err := s.ActionsFor(ctx, userA, nil)
	require.NoError(t, err)
	require.Empty(t, empty)
}

func TestReadsSurfaceActions(t *testing.T) {
	ctx := context.Background()
	s := newStore(t)
	resetDB(t, rawPool(t))

	require.NoError(t, s.Deliver(ctx, summary(userA, videoX, "body")))
	require.NoError(t, s.SetAction(ctx, userA, videoX, "watched"))
	require.NoError(t, s.SetAction(ctx, userA, videoX, "saved"))

	list, err := s.ListSummaries(ctx, userA, 50)
	require.NoError(t, err)
	require.Len(t, list, 1)
	require.Equal(t, []string{"saved", "watched"}, list[0].Actions, "list view carries current actions")

	detail, err := s.GetSummaryByVideo(ctx, userA, videoX)
	require.NoError(t, err)
	require.Equal(t, []string{"saved", "watched"}, detail.Actions, "detail view carries current actions")
}

func TestReadsSurfaceNoActionsAsNil(t *testing.T) {
	ctx := context.Background()
	s := newStore(t)
	resetDB(t, rawPool(t))

	require.NoError(t, s.Deliver(ctx, summary(userA, videoX, "body")))

	list, err := s.ListSummaries(ctx, userA, 50)
	require.NoError(t, err)
	require.Len(t, list, 1)
	require.Nil(t, list[0].Actions, "no actions -> nil slice")
}
