package credentialgeneration

import (
	"bytes"
	"database/sql"
	"database/sql/driver"
	"errors"
	"fmt"
	"regexp"
	"strings"
	"testing"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/go-sql-driver/mysql"
)

func TestResolveExactUsesBinaryIDAndRevalidatesStoredFact(t *testing.T) {
	db, mock := newCredentialGenerationSchemaMock(t)
	repository := mustCredentialGenerationRepository(t, db)
	generation := mustCredentialGeneration(t, credentialGenerationTestInput("Generation-Exact"))
	expectCredentialGenerationSchema(mock, exactCredentialGenerationSchemaFixture(), 3)
	expectCredentialGenerationLoad(mock, generation, false, nil)

	loaded, found, err := repository.ResolveExact(t.Context(), generation.ID())
	if err != nil || !found || !loaded.equal(generation) {
		t.Fatalf("ResolveExact() = equal:%t found:%t err:%v", loaded.equal(generation), found, err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestResolveExactSetReturnsCompleteGenerationsInCallerOrderAndDefensivelyCopies(t *testing.T) {
	db, mock := newCredentialGenerationSchemaMock(t)
	repository := mustCredentialGenerationRepository(t, db)
	first := mustCredentialGeneration(t, credentialGenerationTestInput("generation-full-set-first"))
	second := mustCredentialGeneration(t, credentialGenerationTestInput("generation-full-set-second"))
	missingID := "generation-full-set-missing"
	ids := []string{second.ID(), missingID, first.ID()}
	expectCredentialGenerationSchema(mock, exactCredentialGenerationSchemaFixture(), 3)
	expectCredentialGenerationExactSetPayload(mock, ids, first, second)
	expectCredentialGenerationExactSetLoad(mock, ids, nil, first, second)

	loaded, found, err := repository.ResolveExactSet(t.Context(), ids)
	if err != nil {
		t.Fatalf("ResolveExactSet() error = %v", err)
	}
	if len(loaded) != len(ids) || len(found) != len(ids) {
		t.Fatalf("ResolveExactSet() lengths = %d, %d, want %d, %d", len(loaded), len(found), len(ids), len(ids))
	}
	if !found[0] || found[1] || !found[2] {
		t.Fatalf("ResolveExactSet() found = %#v, want [true false true]", found)
	}
	if !loaded[0].equal(second) || !loaded[1].equal(Generation{}) || !loaded[2].equal(first) {
		t.Fatal("ResolveExactSet() did not retain caller order and missing-entry position")
	}
	if loaded[0].ReceiptProtocolVersion() != second.ReceiptProtocolVersion() ||
		loaded[0].SecretBackendReference() != second.SecretBackendReference() ||
		loaded[0].TransportConfigurationDigest() != second.TransportConfigurationDigest() ||
		loaded[0].OwnerReceiptDigest() != second.OwnerReceiptDigest() ||
		!bytes.Equal(loaded[0].TransportConfiguration(), second.TransportConfiguration()) ||
		!bytes.Equal(loaded[0].OwnerReceipt(), second.OwnerReceipt()) {
		t.Fatal("ResolveExactSet() lost a complete owner generation field")
	}
	transportCopy := loaded[0].TransportConfiguration()
	receiptCopy := loaded[0].OwnerReceipt()
	transportCopy[0] ^= 0xff
	receiptCopy[0] ^= 0xff
	if !bytes.Equal(loaded[0].TransportConfiguration(), second.TransportConfiguration()) ||
		!bytes.Equal(loaded[0].OwnerReceipt(), second.OwnerReceipt()) {
		t.Fatal("ResolveExactSet() exposed mutable retained binary storage")
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestResolveExactIdentitySetProjectsTheSharedFullGenerationLoad(t *testing.T) {
	db, mock := newCredentialGenerationSchemaMock(t)
	repository := mustCredentialGenerationRepository(t, db)
	first := mustCredentialGeneration(t, credentialGenerationTestInput("generation-set-first"))
	second := mustCredentialGeneration(t, credentialGenerationTestInput("generation-set-second"))
	missingID := "generation-set-missing"
	ids := []string{second.ID(), missingID, first.ID()}
	expectCredentialGenerationSchema(mock, exactCredentialGenerationSchemaFixture(), 3)
	expectCredentialGenerationExactSetPayload(mock, ids, first, second)
	expectCredentialGenerationExactSetLoad(mock, ids, nil, first, second)

	generations, generationFound, err := repository.ResolveExactSet(t.Context(), ids)
	if err != nil {
		t.Fatalf("ResolveExactSet() error = %v", err)
	}
	expectCredentialGenerationSchema(mock, exactCredentialGenerationSchemaFixture(), 3)
	expectCredentialGenerationExactSetPayload(mock, ids, first, second)
	expectCredentialGenerationExactSetLoad(mock, ids, nil, first, second)

	loaded, found, err := repository.ResolveExactIdentitySet(t.Context(), ids)
	if err != nil {
		t.Fatalf("ResolveExactIdentitySet() error = %v", err)
	}
	if len(loaded) != len(generations) || len(found) != len(generationFound) {
		t.Fatalf("ResolveExactIdentitySet() lengths = %d, %d; ResolveExactSet() = %d, %d", len(loaded), len(found), len(generations), len(generationFound))
	}
	for index := range loaded {
		if found[index] != generationFound[index] {
			t.Fatalf("ResolveExactIdentitySet() found[%d] = %t, ResolveExactSet() = %t", index, found[index], generationFound[index])
		}
		if found[index] && loaded[index] != generations[index].exactIdentity() {
			t.Fatalf("ResolveExactIdentitySet()[%d] differs from ResolveExactSet() projection", index)
		}
		if !found[index] && loaded[index] != (ExactIdentity{}) {
			t.Fatalf("ResolveExactIdentitySet()[%d] missing projection is non-zero", index)
		}
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestResolveExactSetRejectsCorruptCompleteGeneration(t *testing.T) {
	db, mock := newCredentialGenerationSchemaMock(t)
	repository := mustCredentialGenerationRepository(t, db)
	generation := mustCredentialGeneration(t, credentialGenerationTestInput("generation-set-corrupt"))
	ids := []string{generation.ID()}
	expectCredentialGenerationSchema(mock, exactCredentialGenerationSchemaFixture(), 3)
	expectCredentialGenerationExactSetPayload(mock, ids, generation)
	expectCredentialGenerationExactSetLoad(mock, ids, func(values []driver.Value) {
		values[7] = "sha256:" + strings.Repeat("0", 64)
	}, generation)

	if _, _, err := repository.ResolveExactSet(t.Context(), ids); !errors.Is(err, ErrStoredGenerationInvalid) {
		t.Fatalf("ResolveExactSet(corrupt) error = %v, want ErrStoredGenerationInvalid", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestResolveExactSetRejectsPayloadBeforeLoadingBlobs(t *testing.T) {
	db, mock := newCredentialGenerationSchemaMock(t)
	repository := mustCredentialGenerationRepository(t, db)
	ids := []string{"generation-set-payload"}
	expectCredentialGenerationSchema(mock, exactCredentialGenerationSchemaFixture(), 3)
	mock.ExpectQuery(regexp.QuoteMeta(credentialGenerationExactSetPayloadQuery(len(ids)))).
		WithArgs(credentialGenerationExactSetDriverArguments(ids)...).
		WillReturnRows(sqlmock.NewRows([]string{"payload_bytes"}).AddRow(MaxExactIdentitySetPayloadBytes + 1))

	if _, _, err := repository.ResolveExactSet(t.Context(), ids); !errors.Is(err, ErrExactSetBudgetExceeded) {
		t.Fatalf("ResolveExactSet(oversized payload) error = %v, want ErrExactSetBudgetExceeded", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestResolveExactSetRechecksLoadedPayloadBudget(t *testing.T) {
	db, mock := newCredentialGenerationSchemaMock(t)
	repository := mustCredentialGenerationRepository(t, db)
	generation := mustCredentialGeneration(t, credentialGenerationTestInput("generation-set-payload-race"))
	ids := []string{generation.ID()}
	expectCredentialGenerationSchema(mock, exactCredentialGenerationSchemaFixture(), 3)
	mock.ExpectQuery(regexp.QuoteMeta(credentialGenerationExactSetPayloadQuery(len(ids)))).
		WithArgs(credentialGenerationExactSetDriverArguments(ids)...).
		WillReturnRows(sqlmock.NewRows([]string{"payload_bytes"}).AddRow(0))
	expectCredentialGenerationExactSetLoad(mock, ids, func(values []driver.Value) {
		values[6] = make([]byte, MaxExactIdentitySetPayloadBytes+1)
	}, generation)

	generations, found, err := repository.ResolveExactSet(t.Context(), ids)
	if !errors.Is(err, ErrExactSetBudgetExceeded) {
		t.Fatalf("ResolveExactSet(changed payload) error = %v, want ErrExactSetBudgetExceeded", err)
	}
	if generations != nil || found != nil {
		t.Fatalf("ResolveExactSet(changed payload) returned partial results: %#v, %#v", generations, found)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestResolveExactSetUsesBoundedBatches(t *testing.T) {
	const count = credentialGenerationExactSetBatchSize + 1
	db, mock := newCredentialGenerationSchemaMock(t)
	repository := mustCredentialGenerationRepository(t, db)
	ids := make([]string, count)
	generations := make([]Generation, count)
	for index := range ids {
		ids[index] = fmt.Sprintf("generation-set-batch-%03d", index)
		generations[index] = mustCredentialGeneration(t, credentialGenerationTestInput(ids[index]))
	}
	expectCredentialGenerationSchema(mock, exactCredentialGenerationSchemaFixture(), 3)
	for start := 0; start < count; start += credentialGenerationExactSetBatchSize {
		end := min(start+credentialGenerationExactSetBatchSize, count)
		expectCredentialGenerationExactSetPayload(mock, ids[start:end], generations[start:end]...)
	}
	for start := 0; start < count; start += credentialGenerationExactSetBatchSize {
		end := min(start+credentialGenerationExactSetBatchSize, count)
		expectCredentialGenerationExactSetLoad(mock, ids[start:end], nil, generations[start:end]...)
	}

	loaded, found, err := repository.ResolveExactSet(t.Context(), ids)
	if err != nil {
		t.Fatalf("ResolveExactSet(batch boundary) error = %v", err)
	}
	for index := range ids {
		if !found[index] || !loaded[index].equal(generations[index]) {
			t.Fatalf(
				"ResolveExactSet(batch boundary)[%d] = id:%q found:%t",
				index,
				loaded[index].ID(),
				found[index],
			)
		}
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestExactIdentitySetQueriesRepeatTheExactBinaryPredicate(t *testing.T) {
	const count = 3
	predicate := credentialGenerationExactSetPredicate(count)
	if got := strings.Count(predicate, "(Id = ? AND BINARY Id = BINARY ?)"); got != count {
		t.Fatalf("exact-set predicate has %d exact binary terms, want %d: %s", got, count, predicate)
	}
	for name, query := range map[string]string{
		"payload": credentialGenerationExactSetPayloadQuery(count),
		"load":    credentialGenerationExactSetLoadQuery(count),
	} {
		if got := strings.Count(query, "?"); got != count*2 {
			t.Fatalf("%s query has %d bind placeholders, want %d", name, got, count*2)
		}
	}
}

func TestResolveExactSetHasClosedInputBudgets(t *testing.T) {
	tooMany := make([]string, MaxExactIdentitySetEntries+1)
	for index := range tooMany {
		tooMany[index] = fmt.Sprintf("generation-%d", index)
	}
	tooManyBytes := make([]string, MaxExactIdentitySetEntries)
	for index := range tooManyBytes {
		tooManyBytes[index] = strings.Repeat("a", 128) + fmt.Sprintf("-%04d", index)
	}

	tests := []struct {
		name string
		ids  []string
		want error
	}{
		{name: "nil", ids: nil, want: ErrInvalidGeneration},
		{name: "duplicate", ids: []string{"generation-a", "generation-a"}, want: ErrInvalidGeneration},
		{name: "invalid", ids: []string{"generation-a", " generation-b"}, want: ErrInvalidGeneration},
		{name: "entry count", ids: tooMany, want: ErrExactSetBudgetExceeded},
		{name: "ID bytes", ids: tooManyBytes, want: ErrExactSetBudgetExceeded},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			db, mock := newCredentialGenerationSchemaMock(t)
			repository := mustCredentialGenerationRepository(t, db)
			if _, _, err := repository.ResolveExactSet(t.Context(), test.ids); !errors.Is(err, test.want) {
				t.Fatalf("ResolveExactSet() error = %v, want %v", err, test.want)
			}
			if err := mock.ExpectationsWereMet(); err != nil {
				t.Fatalf("unexpected SQL before exact-set input validation: %v", err)
			}
		})
	}
}

func TestResolveExactSetAcceptsNonNilEmptySet(t *testing.T) {
	db, mock := newCredentialGenerationSchemaMock(t)
	repository := mustCredentialGenerationRepository(t, db)
	expectCredentialGenerationSchema(mock, exactCredentialGenerationSchemaFixture(), 3)

	loaded, found, err := repository.ResolveExactSet(t.Context(), []string{})
	if err != nil {
		t.Fatalf("ResolveExactSet(empty) error = %v", err)
	}
	if loaded == nil || found == nil || len(loaded) != 0 || len(found) != 0 {
		t.Fatalf("ResolveExactSet(empty) = %#v, %#v, want non-nil empty slices", loaded, found)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestLoadExactRejectsEveryDerivedFieldMismatch(t *testing.T) {
	generation := mustCredentialGeneration(t, credentialGenerationTestInput("generation-corrupt"))
	tests := []struct {
		name   string
		mutate func([]driver.Value)
	}{
		{name: "configuration digest", mutate: func(values []driver.Value) { values[7] = "sha256:" + string(make([]byte, 64)) }},
		{name: "receipt protocol", mutate: func(values []driver.Value) { values[8] = "nexuscrm.connector-cred-gen.v2" }},
		{name: "owner receipt", mutate: func(values []driver.Value) { values[9] = []byte("different") }},
		{name: "owner receipt digest", mutate: func(values []driver.Value) { values[10] = "sha256:" + string(make([]byte, 64)) }},
		{name: "backend coordinate", mutate: func(values []driver.Value) { values[5] = "secret-version-other" }},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			db, mock := newCredentialGenerationSchemaMock(t)
			expectCredentialGenerationLoad(mock, generation, false, test.mutate)
			if _, _, err := loadExact(t.Context(), db, generation.ID(), false); !errors.Is(err, ErrStoredGenerationInvalid) {
				t.Fatalf("loadExact() error = %v, want ErrStoredGenerationInvalid", err)
			}
			if err := mock.ExpectationsWereMet(); err != nil {
				t.Fatalf("sql expectations: %v", err)
			}
		})
	}
}

func TestPublishInsertsAndReloadsCompleteGeneration(t *testing.T) {
	db, mock := newCredentialGenerationSchemaMock(t)
	repository := mustCredentialGenerationRepository(t, db)
	generation := mustCredentialGeneration(t, credentialGenerationTestInput("generation-publish"))
	expectCredentialGenerationPublicationSchema(mock)
	expectCredentialGenerationPessimisticBegin(mock)
	mock.ExpectExec(`^INSERT INTO _System_ConnectorCredentialGeneration `).
		WithArgs(credentialGenerationInsertArguments(generation)...).
		WillReturnResult(sqlmock.NewResult(0, 1))
	expectDispatchGateInsert(mock, generation.ID(), sqlmock.NewResult(0, 1), nil)
	expectCredentialGenerationLoad(mock, generation, true, nil)
	expectDispatchGateLoad(mock, dispatchGateTestValue(generation.ID(), DispatchModeEnabled, 0), true)
	mock.ExpectCommit()
	expectCredentialGenerationPessimisticCleanup(mock)

	stored, duplicate, err := repository.Publish(t.Context(), generation)
	if err != nil || duplicate || !stored.equal(generation) {
		t.Fatalf("Publish() = equal:%t duplicate:%t err:%v", stored.equal(generation), duplicate, err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestPublishBindsEmptyTransportConfigurationAsNonNullBlob(t *testing.T) {
	db, mock := newCredentialGenerationSchemaMock(t)
	repository := mustCredentialGenerationRepository(t, db)
	input := credentialGenerationTestInput("generation-empty-transport")
	input.TransportConfiguration = nil
	generation := mustCredentialGeneration(t, input)
	if configuration := generation.TransportConfiguration(); configuration == nil || len(configuration) != 0 {
		t.Fatalf("TransportConfiguration() = %#v, want non-nil empty bytes", configuration)
	}

	expectCredentialGenerationPublicationSchema(mock)
	expectCredentialGenerationPessimisticBegin(mock)
	arguments := credentialGenerationInsertArguments(generation)
	arguments[6] = nonNullEmptyBytesArgument{}
	mock.ExpectExec(`^INSERT INTO _System_ConnectorCredentialGeneration `).
		WithArgs(arguments...).
		WillReturnResult(sqlmock.NewResult(0, 1))
	expectDispatchGateInsert(mock, generation.ID(), sqlmock.NewResult(0, 1), nil)
	expectCredentialGenerationLoad(mock, generation, true, nil)
	expectDispatchGateLoad(mock, dispatchGateTestValue(generation.ID(), DispatchModeEnabled, 0), true)
	mock.ExpectCommit()
	expectCredentialGenerationPessimisticCleanup(mock)

	stored, duplicate, err := repository.Publish(t.Context(), generation)
	if err != nil || duplicate || !stored.equal(generation) {
		t.Fatalf("Publish(empty transport) = equal:%t duplicate:%t err:%v", stored.equal(generation), duplicate, err)
	}
	if configuration := stored.TransportConfiguration(); configuration == nil || len(configuration) != 0 {
		t.Fatalf("stored TransportConfiguration() = %#v, want non-nil empty bytes", configuration)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestPublishConvergesOnlyTypedExactDuplicate(t *testing.T) {
	db, mock := newCredentialGenerationSchemaMock(t)
	repository := mustCredentialGenerationRepository(t, db)
	generation := mustCredentialGeneration(t, credentialGenerationTestInput("generation-duplicate"))
	expectCredentialGenerationPublicationSchema(mock)
	expectCredentialGenerationPessimisticBegin(mock)
	mock.ExpectExec(`^INSERT INTO _System_ConnectorCredentialGeneration `).
		WillReturnError(&mysql.MySQLError{Number: 1062, Message: "duplicate primary key"})
	expectCredentialGenerationLoad(mock, generation, true, nil)
	expectDispatchGateInsert(mock, generation.ID(), sqlmock.NewResult(0, 1), nil)
	expectDispatchGateLoad(mock, dispatchGateTestValue(generation.ID(), DispatchModeEnabled, 0), true)
	mock.ExpectCommit()
	expectCredentialGenerationPessimisticCleanup(mock)

	stored, duplicate, err := repository.Publish(t.Context(), generation)
	if err != nil || !duplicate || !stored.equal(generation) {
		t.Fatalf("Publish() = equal:%t duplicate:%t err:%v", stored.equal(generation), duplicate, err)
	}
	if isMySQLDuplicateKey(errors.New("Error 1062 duplicate entry")) {
		t.Fatal("untyped duplicate-looking text was classified as MySQL 1062")
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestPublishExactDuplicatePreservesExistingRevokedGate(t *testing.T) {
	db, mock := newCredentialGenerationSchemaMock(t)
	repository := mustCredentialGenerationRepository(t, db)
	generation := mustCredentialGeneration(t, credentialGenerationTestInput("generation-revoked-duplicate"))
	revoked := dispatchGateTestValue(generation.ID(), DispatchModeRevoked, 1)
	expectCredentialGenerationPublicationSchema(mock)
	expectCredentialGenerationPessimisticBegin(mock)
	mock.ExpectExec(`^INSERT INTO _System_ConnectorCredentialGeneration `).
		WillReturnError(&mysql.MySQLError{Number: 1062, Message: "duplicate primary key"})
	expectCredentialGenerationLoad(mock, generation, true, nil)
	expectDispatchGateInsert(
		mock,
		generation.ID(),
		nil,
		&mysql.MySQLError{Number: 1062, Message: "duplicate primary key"},
	)
	expectDispatchGateLoad(mock, revoked, true)
	mock.ExpectCommit()
	expectCredentialGenerationPessimisticCleanup(mock)

	stored, duplicate, err := repository.Publish(t.Context(), generation)
	if err != nil || !duplicate || !stored.equal(generation) {
		t.Fatalf("Publish() = equal:%t duplicate:%t err:%v", stored.equal(generation), duplicate, err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestPublishRejectsSameIDWithDifferentImmutableBytes(t *testing.T) {
	db, mock := newCredentialGenerationSchemaMock(t)
	repository := mustCredentialGenerationRepository(t, db)
	input := credentialGenerationTestInput("generation-conflict")
	generation := mustCredentialGeneration(t, input)
	input.SecretBackendVersionRef = "secret-version-0002"
	other := mustCredentialGeneration(t, input)
	expectCredentialGenerationPublicationSchema(mock)
	expectCredentialGenerationPessimisticBegin(mock)
	mock.ExpectExec(`^INSERT INTO _System_ConnectorCredentialGeneration `).
		WillReturnError(&mysql.MySQLError{Number: 1062, Message: "duplicate primary key"})
	expectCredentialGenerationLoad(mock, other, true, nil)
	mock.ExpectRollback()
	expectCredentialGenerationPessimisticCleanup(mock)

	if _, _, err := repository.Publish(t.Context(), generation); !errors.Is(err, ErrIdentityConflict) {
		t.Fatalf("Publish() error = %v, want ErrIdentityConflict", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestPublishRollsBackNonDuplicateInsertFailure(t *testing.T) {
	db, mock := newCredentialGenerationSchemaMock(t)
	repository := mustCredentialGenerationRepository(t, db)
	generation := mustCredentialGeneration(t, credentialGenerationTestInput("generation-insert-failure"))
	expectCredentialGenerationPublicationSchema(mock)
	expectCredentialGenerationPessimisticBegin(mock)
	mock.ExpectExec(`^INSERT INTO _System_ConnectorCredentialGeneration `).
		WillReturnError(errors.New("storage unavailable"))
	mock.ExpectRollback()
	expectCredentialGenerationPessimisticCleanup(mock)

	if _, _, err := repository.Publish(t.Context(), generation); err == nil ||
		errors.Is(err, ErrIdentityConflict) {
		t.Fatalf("Publish() error = %v, want non-conflict storage error", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestPublishRollsBackGenerationWhenDispatchGateInitializationFails(t *testing.T) {
	db, mock := newCredentialGenerationSchemaMock(t)
	repository := mustCredentialGenerationRepository(t, db)
	generation := mustCredentialGeneration(t, credentialGenerationTestInput("generation-gate-failure"))
	expectCredentialGenerationPublicationSchema(mock)
	expectCredentialGenerationPessimisticBegin(mock)
	mock.ExpectExec(`^INSERT INTO _System_ConnectorCredentialGeneration `).
		WithArgs(credentialGenerationInsertArguments(generation)...).
		WillReturnResult(sqlmock.NewResult(0, 1))
	expectDispatchGateInsert(mock, generation.ID(), nil, errors.New("gate storage unavailable"))
	mock.ExpectRollback()
	expectCredentialGenerationPessimisticCleanup(mock)

	if _, _, err := repository.Publish(t.Context(), generation); err == nil {
		t.Fatal("Publish() error = nil, want gate initialization failure")
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestRepositoryRejectsMissingDependenciesAndInvalidInputBeforeSQL(t *testing.T) {
	if _, err := NewRepository(nil); !errors.Is(err, ErrDependencyMissing) {
		t.Fatalf("NewRepository(nil) error = %v", err)
	}
	db, mock := newCredentialGenerationSchemaMock(t)
	repository := mustCredentialGenerationRepository(t, db)
	if _, _, err := repository.ResolveExact(t.Context(), " generation"); !errors.Is(err, ErrInvalidGeneration) {
		t.Fatalf("ResolveExact(invalid ID) error = %v", err)
	}
	var nilRepository *Repository
	if _, _, err := nilRepository.ResolveExactSet(t.Context(), []string{}); !errors.Is(err, ErrDependencyMissing) {
		t.Fatalf("nil Repository ResolveExactSet() error = %v", err)
	}
	if _, _, err := repository.ResolveExactSet(nil, []string{}); !errors.Is(err, ErrDependencyMissing) {
		t.Fatalf("ResolveExactSet(nil context) error = %v", err)
	}
	if _, _, err := nilRepository.ResolveExactIdentitySet(t.Context(), []string{}); !errors.Is(err, ErrDependencyMissing) {
		t.Fatalf("nil Repository ResolveExactIdentitySet() error = %v", err)
	}
	if _, _, err := repository.ResolveExactIdentitySet(nil, []string{}); !errors.Is(err, ErrDependencyMissing) {
		t.Fatalf("ResolveExactIdentitySet(nil context) error = %v", err)
	}
	if _, _, err := repository.Publish(t.Context(), Generation{}); !errors.Is(err, ErrInvalidGeneration) {
		t.Fatalf("Publish(zero generation) error = %v", err)
	}
	if errors.Is(repository.VerifySchema(nil), ErrSchemaUnavailable) {
		t.Fatal("nil public context was misreported as schema drift")
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("unexpected SQL before input validation: %v", err)
	}
}

func expectCredentialGenerationLoad(
	mock sqlmock.Sqlmock,
	generation Generation,
	forUpdate bool,
	mutate func([]driver.Value),
) {
	query := credentialGenerationSelect
	if forUpdate {
		query += " FOR UPDATE"
	}
	values := credentialGenerationRowValues(generation)
	if mutate != nil {
		mutate(values)
	}
	rows := sqlmock.NewRows([]string{
		"Id", "StableCredentialIdentity", "ProviderScopeId", "ConnectorAccountId",
		"SecretBackendProtocol", "SecretBackendVersionRef",
		"TransportConfiguration", "TransportConfigurationDigest",
		"ReceiptProtocolVersion", "OwnerReceipt", "OwnerReceiptDigest",
	}).AddRow(values...)
	mock.ExpectQuery(regexp.QuoteMeta(query)).
		WithArgs(generation.ID(), generation.ID()).
		WillReturnRows(rows)
}

func expectCredentialGenerationExactSetPayload(
	mock sqlmock.Sqlmock,
	ids []string,
	generations ...Generation,
) {
	payloadBytes := 0
	for _, generation := range generations {
		payloadBytes += len(generation.transportConfiguration) + len(generation.ownerReceipt)
	}
	mock.ExpectQuery(regexp.QuoteMeta(credentialGenerationExactSetPayloadQuery(len(ids)))).
		WithArgs(credentialGenerationExactSetDriverArguments(ids)...).
		WillReturnRows(sqlmock.NewRows([]string{"payload_bytes"}).AddRow(payloadBytes))
}

func expectCredentialGenerationExactSetLoad(
	mock sqlmock.Sqlmock,
	ids []string,
	mutate func([]driver.Value),
	generations ...Generation,
) {
	rows := sqlmock.NewRows([]string{
		"Id", "StableCredentialIdentity", "ProviderScopeId", "ConnectorAccountId",
		"SecretBackendProtocol", "SecretBackendVersionRef",
		"TransportConfiguration", "TransportConfigurationDigest",
		"ReceiptProtocolVersion", "OwnerReceipt", "OwnerReceiptDigest",
	})
	for _, generation := range generations {
		values := credentialGenerationRowValues(generation)
		if mutate != nil {
			mutate(values)
		}
		rows.AddRow(values...)
	}
	mock.ExpectQuery(regexp.QuoteMeta(credentialGenerationExactSetLoadQuery(len(ids)))).
		WithArgs(credentialGenerationExactSetDriverArguments(ids)...).
		WillReturnRows(rows)
}

func credentialGenerationExactSetDriverArguments(ids []string) []driver.Value {
	arguments := make([]driver.Value, 0, len(ids)*2)
	for _, id := range ids {
		arguments = append(arguments, id, id)
	}
	return arguments
}

func expectCredentialGenerationPublicationSchema(mock sqlmock.Sqlmock) {
	expectCredentialGenerationSchema(mock, exactCredentialGenerationSchemaFixture(), 3)
	expectDispatchGateSchema(mock, exactDispatchGateSchemaFixture(), 3)
}

func credentialGenerationRowValues(generation Generation) []driver.Value {
	return []driver.Value{
		generation.ID(),
		generation.StableCredentialIdentity(),
		generation.ProviderScopeID(),
		generation.ConnectorAccountID(),
		generation.SecretBackendProtocol(),
		generation.SecretBackendVersionRef(),
		generation.TransportConfiguration(),
		generation.TransportConfigurationDigest(),
		string(generation.ReceiptProtocolVersion()),
		generation.OwnerReceipt(),
		generation.OwnerReceiptDigest(),
	}
}

func credentialGenerationInsertArguments(generation Generation) []driver.Value {
	return credentialGenerationRowValues(generation)
}

type nonNullEmptyBytesArgument struct{}

func (nonNullEmptyBytesArgument) Match(value driver.Value) bool {
	bytes, ok := value.([]byte)
	return ok && bytes != nil && len(bytes) == 0
}

func expectCredentialGenerationPessimisticBegin(mock sqlmock.Sqlmock) {
	mock.ExpectQuery(regexp.QuoteMeta("SELECT @@SESSION.tidb_constraint_check_in_place_pessimistic")).
		WillReturnRows(sqlmock.NewRows([]string{"constraint_check"}).AddRow("OFF"))
	mock.ExpectExec(regexp.QuoteMeta("SET SESSION tidb_txn_mode = 'pessimistic'")).
		WillReturnResult(sqlmock.NewResult(0, 0))
	mock.ExpectExec(regexp.QuoteMeta("SET SESSION tidb_constraint_check_in_place_pessimistic = ON")).
		WillReturnResult(sqlmock.NewResult(0, 0))
	mock.ExpectBegin()
}

func expectCredentialGenerationPessimisticCleanup(mock sqlmock.Sqlmock) {
	mock.ExpectExec(regexp.QuoteMeta("SET SESSION tidb_constraint_check_in_place_pessimistic = OFF")).
		WillReturnResult(sqlmock.NewResult(0, 0))
	mock.ExpectExec(regexp.QuoteMeta("SET SESSION tidb_txn_mode = DEFAULT")).
		WillReturnResult(sqlmock.NewResult(0, 0))
}

func mustCredentialGenerationRepository(t *testing.T, db *sql.DB) *Repository {
	t.Helper()
	repository, err := NewRepository(db)
	if err != nil {
		t.Fatalf("NewRepository: %v", err)
	}
	return repository
}
