package packs

import (
	"context"
	"testing"

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

// TestConnectorCapabilitySweeperApply_NoOpWhenManifestMatchesDB verifies that
// no UPDATE fires when every live DB row is also declared in the manifest.
func TestConnectorCapabilitySweeperApply_NoOpWhenManifestMatchesDB(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	declared := []ConnectorCapabilityComponentPayload{
		{Namespace: "acme-connector", Kind: "signature_verifier"},
		{Namespace: "acme-connector", Kind: "envelope_parser"},
	}

	// DB returns same two rows as declared — sweeper should issue no UPDATE.
	id1 := connectorCapabilityDeclarationID("acme-connector", "acme-connector", "signature_verifier")
	id2 := connectorCapabilityDeclarationID("acme-connector", "acme-connector", "envelope_parser")
	mock.ExpectQuery("SELECT Id, Namespace, Kind FROM _System_ConnectorCapabilityDeclaration").
		WithArgs("acme-connector").
		WillReturnRows(sqlmock.NewRows([]string{"Id", "Namespace", "Kind"}).
			AddRow(id1, "acme-connector", "signature_verifier").
			AddRow(id2, "acme-connector", "envelope_parser"))

	if err := applyConnectorCapabilityPackSweep(context.Background(), db, "acme-connector", declared); err != nil {
		t.Fatalf("applyConnectorCapabilityPackSweep: %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations (no UPDATE expected): %v", err)
	}
}

// TestConnectorCapabilitySweeperApply_SoftDeletesDroppedCapability verifies
// that a row present in DB but absent from the declared set gets soft-deleted.
func TestConnectorCapabilitySweeperApply_SoftDeletesDroppedCapability(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	// Manifest now only declares signature_verifier; envelope_parser was dropped.
	declared := []ConnectorCapabilityComponentPayload{
		{Namespace: "acme-connector", Kind: "signature_verifier"},
	}

	idSig := connectorCapabilityDeclarationID("acme-connector", "acme-connector", "signature_verifier")
	idEnv := connectorCapabilityDeclarationID("acme-connector", "acme-connector", "envelope_parser")

	mock.ExpectQuery("SELECT Id, Namespace, Kind FROM _System_ConnectorCapabilityDeclaration").
		WithArgs("acme-connector").
		WillReturnRows(sqlmock.NewRows([]string{"Id", "Namespace", "Kind"}).
			AddRow(idSig, "acme-connector", "signature_verifier").
			AddRow(idEnv, "acme-connector", "envelope_parser"))

	mock.ExpectExec("UPDATE _System_ConnectorCapabilityDeclaration").
		WithArgs("acme-connector", idEnv).
		WillReturnResult(sqlmock.NewResult(0, 1))

	if err := applyConnectorCapabilityPackSweep(context.Background(), db, "acme-connector", declared); err != nil {
		t.Fatalf("applyConnectorCapabilityPackSweep: %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

// TestConnectorCapabilitySweeperApply_SoftDeletesAllWhenManifestEmpty verifies
// that when declared is empty, every owner-pack live row is soft-deleted.
func TestConnectorCapabilitySweeperApply_SoftDeletesAllWhenManifestEmpty(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	idSig := connectorCapabilityDeclarationID("acme-connector", "acme-connector", "signature_verifier")
	idEnv := connectorCapabilityDeclarationID("acme-connector", "acme-connector", "envelope_parser")

	mock.ExpectQuery("SELECT Id, Namespace, Kind FROM _System_ConnectorCapabilityDeclaration").
		WithArgs("acme-connector").
		WillReturnRows(sqlmock.NewRows([]string{"Id", "Namespace", "Kind"}).
			AddRow(idSig, "acme-connector", "signature_verifier").
			AddRow(idEnv, "acme-connector", "envelope_parser"))

	// Both IDs should be in the UPDATE (sorted).
	mock.ExpectExec("UPDATE _System_ConnectorCapabilityDeclaration").
		WithArgs("acme-connector", idEnv, idSig).
		WillReturnResult(sqlmock.NewResult(0, 2))

	if err := applyConnectorCapabilityPackSweep(context.Background(), db, "acme-connector", nil); err != nil {
		t.Fatalf("applyConnectorCapabilityPackSweep: %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

// TestConnectorCapabilitySweeperVerify_FailsClosedOnLeftoverLiveRow verifies
// that verifyConnectorCapabilityPackSweeper returns an error when a live row
// exists that is not in the declared set.
func TestConnectorCapabilitySweeperVerify_FailsClosedOnLeftoverLiveRow(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	// Manifest only declares signature_verifier; DB has envelope_parser as well.
	declared := []ConnectorCapabilityComponentPayload{
		{Namespace: "acme-connector", Kind: "signature_verifier"},
	}

	mock.ExpectQuery("SELECT Namespace, Kind FROM _System_ConnectorCapabilityDeclaration").
		WithArgs("acme-connector").
		WillReturnRows(sqlmock.NewRows([]string{"Namespace", "Kind"}).
			AddRow("acme-connector", "signature_verifier").
			AddRow("acme-connector", "envelope_parser"))

	if err := verifyConnectorCapabilityPackSweeper(context.Background(), db, "acme-connector", declared); err == nil {
		t.Fatal("verifyConnectorCapabilityPackSweeper returned nil, want error for leftover row")
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}
