package store_test

import (
	"context"
	"errors"
	"testing"

	"github.com/stretchr/testify/require"

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

const (
	subjectA = "dex|alice-123"
	subjectB = "dex|bob-456"
)

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

	id, found, err := s.UserBySubject(ctx, subjectA)
	require.NoError(t, err)
	require.False(t, found)
	require.Empty(t, id)
}

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

	id, err := s.RegisterUser(ctx, subjectA, "Alice")
	require.NoError(t, err)
	require.NotEmpty(t, id)

	// Exactly one users row with the returned id and the given display name.
	var users int
	var name string
	require.NoError(t, p.QueryRow(ctx,
		`SELECT count(*), coalesce(max(display_name), '') FROM users WHERE id = $1`, id).
		Scan(&users, &name))
	require.Equal(t, 1, users)
	require.Equal(t, "Alice", name)

	// Exactly one identity row mapping the subject to that id.
	var idents int
	require.NoError(t, p.QueryRow(ctx,
		`SELECT count(*) FROM user_identities WHERE dex_subject = $1 AND user_id = $2`,
		subjectA, id).Scan(&idents))
	require.Equal(t, 1, idents)

	// And it now resolves straight through.
	got, found, err := s.UserBySubject(ctx, subjectA)
	require.NoError(t, err)
	require.True(t, found)
	require.Equal(t, id, got)
}

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

	first, err := s.RegisterUser(ctx, subjectA, "Alice")
	require.NoError(t, err)

	_, err = s.RegisterUser(ctx, subjectA, "Alice Again")
	require.Error(t, err)
	require.True(t, errors.Is(err, store.ErrSubjectRegistered))

	// No second user was created; the original mapping is intact.
	var users, idents int
	require.NoError(t, p.QueryRow(ctx, `SELECT count(*) FROM users`).Scan(&users))
	require.NoError(t, p.QueryRow(ctx, `SELECT count(*) FROM user_identities`).Scan(&idents))
	require.Equal(t, 1, users)
	require.Equal(t, 1, idents)

	got, found, err := s.UserBySubject(ctx, subjectA)
	require.NoError(t, err)
	require.True(t, found)
	require.Equal(t, first, got)
}

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

	empty, err := s.ListAllUsers(ctx)
	require.NoError(t, err)
	require.Empty(t, empty, "no registrations yet → empty slice")

	const subjectC = "dex|carol-789"
	idA, err := s.RegisterUser(ctx, subjectA, "Alice")
	require.NoError(t, err)
	idB, err := s.RegisterUser(ctx, subjectB, "Bob")
	require.NoError(t, err)
	idC, err := s.RegisterUser(ctx, subjectC, "Carol")
	require.NoError(t, err)

	users, err := s.ListAllUsers(ctx)
	require.NoError(t, err)
	require.Len(t, users, 3)

	// Order is unspecified; compare as a set of (userID, subject) pairs.
	got := make(map[string]string, len(users))
	for _, u := range users {
		got[u.DexSubject] = u.UserID
	}
	require.Equal(t, map[string]string{
		subjectA: idA,
		subjectB: idB,
		subjectC: idC,
	}, got)
}

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

	idA, err := s.RegisterUser(ctx, subjectA, "Alice")
	require.NoError(t, err)
	idB, err := s.RegisterUser(ctx, subjectB, "Bob")
	require.NoError(t, err)
	require.NotEqual(t, idA, idB)

	var users int
	require.NoError(t, p.QueryRow(ctx, `SELECT count(*) FROM users`).Scan(&users))
	require.Equal(t, 2, users)
}
