package store_test

import (
	"context"
	"testing"

	"github.com/jackc/pgx/v5/pgxpool"
	"github.com/stretchr/testify/require"
)

// seedIdentity inserts the un-RLS'd dex_subject → user_id mapping for a user, so
// the cascade-on-delete to user_identities can be asserted.
func seedIdentity(t *testing.T, p *pgxpool.Pool, subject, userID string) {
	t.Helper()
	_, err := p.Exec(context.Background(),
		`INSERT INTO user_identities (dex_subject, user_id) VALUES ($1, $2)`, subject, userID)
	require.NoError(t, err)
}

// countFor counts rows owned by userID in table. The users table is keyed on its
// own id; every other isolated table on user_id.
func countFor(t *testing.T, p *pgxpool.Pool, table, userID string) int {
	t.Helper()
	col := "user_id"
	if table == "users" {
		col = "id"
	}
	var n int
	require.NoError(t, p.QueryRow(context.Background(),
		`SELECT count(*) FROM `+table+` WHERE `+col+` = $1`, userID).Scan(&n))
	return n
}

func countDeliveries(t *testing.T, p *pgxpool.Pool, summaryID string) int {
	t.Helper()
	var n int
	require.NoError(t, p.QueryRow(context.Background(),
		`SELECT count(*) FROM sink_deliveries WHERE summary_id = $1`, summaryID).Scan(&n))
	return n
}

func countIdentities(t *testing.T, p *pgxpool.Pool, userID string) int {
	t.Helper()
	var n int
	require.NoError(t, p.QueryRow(context.Background(),
		`SELECT count(*) FROM user_identities WHERE user_id = $1`, userID).Scan(&n))
	return n
}

// TestDeleteUserRemovesAllRowsForUserOnly is the account-deletion isolation proof
// (Worker N+M): DeleteUser wipes every row owned by the target user — across the
// cascade-linked tables, the user_identities map (ON DELETE CASCADE), AND
// summary_actions (which has NO FK to users, so the users-row cascade does not
// reach it and DeleteUser must delete it explicitly) — while leaving another
// user's rows completely intact.
func TestDeleteUserRemovesAllRowsForUserOnly(t *testing.T) {
	ctx := context.Background()
	newStore(t) // apply migrations
	super := rawPool(t)
	resetDB(t, super)

	a := seedUser(t, super, userA)
	b := seedUser(t, super, userB)
	seedIdentity(t, super, "subject-a", userA)
	seedIdentity(t, super, "subject-b", userB)

	s := newStore(t)
	require.NoError(t, s.DeleteUser(ctx, userA))

	// Every user-keyed isolated table: zero rows for A, exactly one for B.
	for _, table := range userIsolatedTables {
		require.Equal(t, 0, countFor(t, super, table, userA),
			"A's %s rows must be deleted", table)
		require.Equal(t, 1, countFor(t, super, table, userB),
			"B's %s rows must survive A's deletion", table)
	}

	// sink_deliveries is keyed by summary, not user_id (cascade from summaries).
	require.Equal(t, 0, countDeliveries(t, super, a.summaryID), "A's deliveries must cascade-delete")
	require.Equal(t, 1, countDeliveries(t, super, b.summaryID), "B's deliveries must survive")

	// The cascade must reach user_identities (explicitly asserted per the mission).
	require.Equal(t, 0, countIdentities(t, super, userA), "A's identity mapping must cascade-delete")
	require.Equal(t, 1, countIdentities(t, super, userB), "B's identity mapping must survive")
}

func TestDeleteUserIsIdempotent(t *testing.T) {
	ctx := context.Background()
	s := newStore(t)
	resetDB(t, rawPool(t))
	// Deleting an absent user is a no-op, not an error.
	require.NoError(t, s.DeleteUser(ctx, userA))
}

func TestDisplayNameReturnsRegisteredName(t *testing.T) {
	ctx := context.Background()
	s := newStore(t)
	p := rawPool(t)
	resetDB(t, p)
	_, err := p.Exec(ctx, `INSERT INTO users (id, display_name) VALUES ($1, $2)`, userA, "Ada")
	require.NoError(t, err)

	name, err := s.DisplayName(ctx, userA)
	require.NoError(t, err)
	require.Equal(t, "Ada", name)
}
