package store_test

import (
	"context"
	"testing"

	"github.com/stretchr/testify/require"

	"git.d-ma.be/mathias/tapir/internal/adapters/store"
)

// seedUserRow inserts a bare users row (FK target for a connection) as the
// superuser pool, which bypasses RLS.
func seedUserRow(t *testing.T, userID string) {
	t.Helper()
	_, err := rawPool(t).Exec(context.Background(),
		`INSERT INTO users (id) VALUES ($1) ON CONFLICT (id) DO NOTHING`, userID)
	require.NoError(t, err)
}

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

	require.NoError(t, s.UpsertConnection(ctx, userA, store.Connection{
		Provider:        "youtube",
		ProviderAccount: "chan@example.com",
		TokenRef:        "youtube/" + userA + "/refresh_token",
		Status:          "active",
	}))

	conns, err := s.ConnectionsForUser(ctx, userA)
	require.NoError(t, err)
	require.Len(t, conns, 1)
	require.Equal(t, "youtube", conns[0].Provider)
	require.Equal(t, "chan@example.com", conns[0].ProviderAccount)
	require.Equal(t, "youtube/"+userA+"/refresh_token", conns[0].TokenRef)
	require.Equal(t, "active", conns[0].Status)
	require.False(t, conns[0].ConnectedAt.IsZero(), "connected_at set by the DB default")
}

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

	require.NoError(t, s.UpsertConnection(ctx, userA, store.Connection{
		Provider: "youtube", TokenRef: "ref-1", Status: "active",
	}))
	// Re-connect the same provider: must update in place, not duplicate.
	require.NoError(t, s.UpsertConnection(ctx, userA, store.Connection{
		Provider: "youtube", TokenRef: "ref-2", Status: "revoked",
	}))

	var count int
	require.NoError(t, p.QueryRow(ctx,
		`SELECT count(*) FROM video_connections WHERE user_id = $1 AND provider = 'youtube'`,
		userA).Scan(&count))
	require.Equal(t, 1, count, "second connect must update, not duplicate")

	conns, err := s.ConnectionsForUser(ctx, userA)
	require.NoError(t, err)
	require.Len(t, conns, 1)
	require.Equal(t, "ref-2", conns[0].TokenRef, "token_ref overwritten")
	require.Equal(t, "revoked", conns[0].Status, "status overwritten")
}

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

	require.NoError(t, s.UpsertConnection(ctx, userA, store.Connection{
		Provider: "youtube", TokenRef: "ref", Status: "active",
	}))
	require.NoError(t, s.DeleteConnection(ctx, userA, "youtube"))

	conns, err := s.ConnectionsForUser(ctx, userA)
	require.NoError(t, err)
	require.Empty(t, conns)

	// Deleting an absent connection is a no-op, not an error.
	require.NoError(t, s.DeleteConnection(ctx, userA, "youtube"))
}

func TestConnectionsForUserIsScoped(t *testing.T) {
	ctx := context.Background()
	s := newStore(t)
	resetDB(t, rawPool(t))
	seedUserRow(t, userA)
	seedUserRow(t, userB)

	require.NoError(t, s.UpsertConnection(ctx, userA, store.Connection{
		Provider: "youtube", TokenRef: "a-ref", Status: "active",
	}))

	// User B must not see user A's connection.
	connsB, err := s.ConnectionsForUser(ctx, userB)
	require.NoError(t, err)
	require.Empty(t, connsB, "user B must not see user A's connections")

	connsA, err := s.ConnectionsForUser(ctx, userA)
	require.NoError(t, err)
	require.Len(t, connsA, 1)
}
