package packs

import (
	"context"
	"encoding/json"
	"testing"

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

func TestConnectorCapabilityComponent_BusinessKey(t *testing.T) {
	comp := mustNewCCComp(t, "my-connector", "signature_verifier")
	if got, want := comp.BusinessKey(), "my-connector:signature_verifier"; got != want {
		t.Fatalf("BusinessKey() = %q, want %q", got, want)
	}
}

func TestConnectorCapabilityComponent_BusinessKeyUniqueness(t *testing.T) {
	c1 := mustNewCCComp(t, "pack-a", "signature_verifier")
	c2 := mustNewCCComp(t, "pack-a", "envelope_parser")
	if c1.BusinessKey() == c2.BusinessKey() {
		t.Fatalf("BusinessKey collision: %q", c1.BusinessKey())
	}
}

func TestConnectorCapabilityComponent_PayloadHashStable(t *testing.T) {
	c1 := mustNewCCComp(t, "acme-connector", "signature_verifier")
	c2 := mustNewCCComp(t, "acme-connector", "signature_verifier")
	h1 := c1.(*connectorCapabilityComponent).payloadHash
	h2 := c2.(*connectorCapabilityComponent).payloadHash
	if h1 == "" || h1 != h2 {
		t.Fatalf("payloadHash not stable: %q vs %q", h1, h2)
	}
}

func TestConnectorCapabilityComponent_ApplyUpsertCreatesRow(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	mock.ExpectExec("INSERT INTO _System_ConnectorCapabilityDeclaration").
		WithArgs(sqlmock.AnyArg(), "acme-connector", "acme-connector", "signature_verifier", sqlmock.AnyArg()).
		WillReturnResult(sqlmock.NewResult(1, 1))

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

func TestConnectorCapabilityComponent_LiveProjectionPresent(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	comp := mustNewCCComp(t, "acme-connector", "signature_verifier")
	c := comp.(*connectorCapabilityComponent)
	rowID := connectorCapabilityDeclarationID("acme-connector", "acme-connector", "signature_verifier")
	mock.ExpectQuery("SELECT Id, InstalledPackName, Namespace, Kind, PayloadHash, COALESCE").
		WithArgs("acme-connector", "acme-connector", "signature_verifier").
		WillReturnRows(sqlmock.NewRows([]string{"Id", "InstalledPackName", "Namespace", "Kind", "PayloadHash", "IsDeleted"}).
			AddRow(rowID, "acme-connector", "acme-connector", "signature_verifier", c.payloadHash, 0))

	got, err := comp.LiveProjection(context.Background(), VerifyDeps{DB: db})
	if err != nil || got == nil {
		t.Fatalf("LiveProjection err=%v got=%v", err, got)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestConnectorCapabilityComponent_LiveProjectionMissing(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	mock.ExpectQuery("SELECT Id, InstalledPackName, Namespace, Kind, PayloadHash, COALESCE").
		WithArgs("acme-connector", "acme-connector", "signature_verifier").
		WillReturnRows(sqlmock.NewRows([]string{"Id", "InstalledPackName", "Namespace", "Kind", "PayloadHash", "IsDeleted"}))

	got, err := mustNewCCComp(t, "acme-connector", "signature_verifier").LiveProjection(context.Background(), VerifyDeps{DB: db})
	if err != nil || got != nil {
		t.Fatalf("LiveProjection err=%v got=%v, want nil row", err, got)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestConnectorCapabilityComponent_ExpectedPayloadRoundtrip(t *testing.T) {
	comp := mustNewCCComp(t, "acme-connector", "envelope_parser")
	raw, _ := comp.ExpectedPayload()
	var p ConnectorCapabilityComponentPayload
	if err := json.Unmarshal(raw, &p); err != nil || p.Kind != "envelope_parser" {
		t.Fatalf("payload roundtrip: err=%v p=%+v", err, p)
	}
}

// mustNewCCComp creates a connector_capability component for testing.
func mustNewCCComp(t *testing.T, packName, kind string) PackComponent {
	t.Helper()
	payload := []byte(`{"namespace":"` + packName + `","kind":"` + kind + `"}`)
	comp, err := newConnectorCapabilityComponent(packName, ManifestComponent{
		Kind:          ComponentKindConnectorCapability,
		BusinessKey:   packName + ":" + kind,
		Manageability: ManageabilityLocked,
	}, payload)
	if err != nil {
		t.Fatalf("newConnectorCapabilityComponent: %v", err)
	}
	return comp
}
