package packs

import (
	"context"
	"testing"

	sqlmock "github.com/DATA-DOG/go-sqlmock"
)

// Test 9: Sweeper soft-deletes orphan rows (endpointClasses no longer declared).
func TestConnectorRateLimitPackSweeper_SoftDeletesOrphans(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	packName := "test-connector"
	batch := &connectorRateLimitPackBatch{
		declaredEndpointClasses: []string{"endpointb"}, // only B declared; A is orphan
	}

	// Query returns both rows; A is not in declared set.
	mock.ExpectQuery(`SELECT Id, EndpointClass FROM _System_ConnectorRateLimit`).
		WithArgs(packName).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "EndpointClass"}).
			AddRow("id-a", "endpointa").
			AddRow("id-b", "endpointb"))

	// Expect soft-delete only for the orphan (id-a).
	mock.ExpectExec(`UPDATE _System_ConnectorRateLimit`).
		WithArgs("id-a").
		WillReturnResult(sqlmock.NewResult(1, 1))

	if err := applyConnectorRateLimitPackSweeper(context.Background(), db, batch, packName); err != nil {
		t.Fatalf("applyConnectorRateLimitPackSweeper: %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Errorf("unmet mock expectations: %v", err)
	}
}

// Test 9b: Verify sweeper returns error when orphans still live.
func TestConnectorRateLimitPackSweeper_VerifyRejectsOrphans(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	packName := "test-connector"
	batch := &connectorRateLimitPackBatch{
		declaredEndpointClasses: []string{"endpointb"},
	}

	mock.ExpectQuery(`SELECT Id, EndpointClass FROM _System_ConnectorRateLimit`).
		WithArgs(packName).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "EndpointClass"}).
			AddRow("id-a", "endpointa").
			AddRow("id-b", "endpointb"))

	err = verifyConnectorRateLimitPackSweeper(context.Background(), db, batch, packName)
	if err == nil {
		t.Fatal("expected error when orphan rows exist, got nil")
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Errorf("unmet mock expectations: %v", err)
	}
}

// Test 9c: Verify sweeper passes when all rows are declared.
func TestConnectorRateLimitPackSweeper_VerifyPassesWhenNoneOrphaned(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	packName := "test-connector"
	batch := &connectorRateLimitPackBatch{
		declaredEndpointClasses: []string{"endpointa", "endpointb"},
	}

	mock.ExpectQuery(`SELECT Id, EndpointClass FROM _System_ConnectorRateLimit`).
		WithArgs(packName).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "EndpointClass"}).
			AddRow("id-a", "endpointa").
			AddRow("id-b", "endpointb"))

	if err := verifyConnectorRateLimitPackSweeper(context.Background(), db, batch, packName); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Errorf("unmet mock expectations: %v", err)
	}
}

// Test 9d: a mixed-case pack Name must match its lower-trimmed OwnerPack rows.
// connector_rate_limit_component.go stores OwnerPack lower-trimmed; the sweeper
// query must normalize the lookup key the same way or it silently leaves orphan
// rows live for mixed-case pack names (codex Review PR #1537).
func TestConnectorRateLimitPackSweeper_MixedCasePackNameMatchesLoweredOwnerPack(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	batch := &connectorRateLimitPackBatch{
		declaredEndpointClasses: []string{"endpointb"}, // A is orphan
	}

	// The query must use the LOWERED owner ("acme-connector"), not the raw
	// mixed-case pack Name ("Acme-Connector").
	mock.ExpectQuery(`SELECT Id, EndpointClass FROM _System_ConnectorRateLimit`).
		WithArgs("acme-connector").
		WillReturnRows(sqlmock.NewRows([]string{"Id", "EndpointClass"}).
			AddRow("id-a", "endpointa").
			AddRow("id-b", "endpointb"))
	mock.ExpectExec(`UPDATE _System_ConnectorRateLimit`).
		WithArgs("id-a").
		WillReturnResult(sqlmock.NewResult(1, 1))

	// Pass the mixed-case Name; the sweeper must normalize it for the query.
	if err := applyConnectorRateLimitPackSweeper(context.Background(), db, batch, "Acme-Connector"); err != nil {
		t.Fatalf("applyConnectorRateLimitPackSweeper: %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Errorf("unmet mock expectations: %v", err)
	}
}
