package packs

import (
	"context"
	"strings"
	"testing"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/nexuscrm/shared/pkg/models"
)

func TestValidateInstallablePackSourceAcceptsLayoutAssignment(t *testing.T) {
	root := writePackFixture(t, "layout-assignment-pack", validPackManifest(`[
    {
      "kind": "object_spec",
      "businessKey": "order",
      "manageability": "locked",
      "payload": `+testObjectSpecPayloadWithFields("Order",
		`{"apiName": "Name", "label": "Name", "type": "string", "required": true, "isNameField": true}`,
	)+`
    },
    {
      "kind": "record_type",
      "businessKey": "record_types",
      "manageability": "locked",
      "dependsOn": [{"kind":"object_spec","businessKey":"order"}],
      "payload": {"recordTypes":[{"objectApiName":"Order","types":[{"name":"Expansion"},{"name":"New"},{"name":"Renewal"}]}]}
    },
    {
      "kind": "page_layout",
      "businessKey": "Order/default",
      "manageability": "defaults_only",
      "dependsOn": [{"kind":"object_spec","businessKey":"order"}],
      "payload": {"objectApiName":"Order","layoutName":"default","isDefault":true,"sections":[]}
    },
    {
      "kind": "layout_assignment",
      "businessKey": "order_rt_routes",
      "manageability": "locked",
      "dependsOn": [
        {"kind":"record_type","businessKey":"record_types"},
        {"kind":"page_layout","businessKey":"Order/default"}
      ],
      "payload": {
        "assignments":[
          {"objectApiName":"Order","recordType":"Expansion"},
          {"objectApiName":"Order","recordType":"New"},
          {"objectApiName":"Order","recordType":"Renewal"}
        ]
      }
    }
  ]`, `"dependencies": [],`))

	report := ValidateInstallablePackSourceWithOptions("local_path:"+root, testPackSourceOptions(t))
	if report.HasErrors() {
		t.Fatalf("ValidateInstallablePackSourceWithOptions rejected layout_assignment source:\n%s", report.Error())
	}
	if report.Pack == nil {
		t.Fatal("ValidateInstallablePackSourceWithOptions did not construct installable pack")
	}
}

func TestSourceBackedPackInstallPlanUsesSyntheticLayoutAssignmentSweeper(t *testing.T) {
	source := &ResolvedPackSource{
		Kind: PackSourceKindLocalPath,
		URI:  "/tmp/source-pack",
		Manifest: &PackManifest{
			ManifestFormatVersion: SupportedManifestFormatVersion,
			Name:                  "layout-assignment-pack",
			DisplayName:           "Layout Assignment Pack",
			Category:              AppPack,
			Version:               "0.1.0",
			PlatformCompatibility: PlatformCompatibility{Min: "0.1.0", MaxExclusive: "0.2.0"},
			Components: []ManifestComponent{
				testObjectSpecManifestComponent("order", "Order"),
				{
					Kind:          ComponentKindRecordType,
					BusinessKey:   "record_types",
					Manageability: ManageabilityLocked,
					DependsOn:     []ComponentReference{{Kind: ComponentKindObjectSpec, BusinessKey: "order"}},
					Payload:       []byte(`{"recordTypes":[{"objectApiName":"Order","types":[{"name":"Expansion"},{"name":"New"}]}]}`),
				},
				{
					Kind:          ComponentKindPageLayout,
					BusinessKey:   "Order/default",
					Manageability: ManageabilityDefaultsOnly,
					DependsOn:     []ComponentReference{{Kind: ComponentKindObjectSpec, BusinessKey: "order"}},
					Payload:       []byte(`{"objectApiName":"Order","layoutName":"default","isDefault":true,"sections":[]}`),
				},
				{
					Kind:          ComponentKindLayoutAssignment,
					BusinessKey:   "order_expansion_route",
					Manageability: ManageabilityLocked,
					DependsOn: []ComponentReference{
						{Kind: ComponentKindRecordType, BusinessKey: "record_types"},
						{Kind: ComponentKindPageLayout, BusinessKey: "Order/default"},
					},
					Payload: []byte(`{"assignments":[{"objectApiName":"Order","recordType":"Expansion"}]}`),
				},
				{
					Kind:          ComponentKindLayoutAssignment,
					BusinessKey:   "order_new_route",
					Manageability: ManageabilityLocked,
					DependsOn: []ComponentReference{
						{Kind: ComponentKindRecordType, BusinessKey: "record_types"},
						{Kind: ComponentKindPageLayout, BusinessKey: "Order/default"},
					},
					Payload: []byte(`{"assignments":[{"objectApiName":"Order","recordType":"New"}]}`),
				},
			},
		},
	}
	pack, err := NewSourceBackedPack(source)
	if err != nil {
		t.Fatalf("NewSourceBackedPack: %v", err)
	}
	plan, err := pack.InstallPlan()
	if err != nil {
		t.Fatalf("InstallPlan: %v", err)
	}

	var uiKeys []string
	var layoutComponents []PackComponent
	for _, component := range plan.ComponentsForPhase(InstallPlanPhaseUISurfaces) {
		uiKeys = append(uiKeys, component.BusinessKey())
		if component.Kind() == ComponentKindLayoutAssignment {
			layoutComponents = append(layoutComponents, component)
		}
	}
	if len(layoutComponents) != 3 {
		t.Fatalf("layout_assignment components in plan = %d, want 3", len(layoutComponents))
	}

	var emptyGroupKeys, batchSweepers int
	for _, component := range layoutComponents {
		grouped := component.(componentApplyGroupProvider)
		if component.BusinessKey() == "__layout_assignment_batch__" {
			batchSweepers++
			if grouped.ApplyGroupKey() != string(ComponentKindLayoutAssignment) {
				t.Fatalf("synthetic sweeper group key = %q, want %q", grouped.ApplyGroupKey(), string(ComponentKindLayoutAssignment))
			}
			continue
		}
		if grouped.ApplyGroupKey() == "" {
			emptyGroupKeys++
		}
	}
	if batchSweepers != 1 || emptyGroupKeys != 2 {
		t.Fatalf("layout_assignment apply-group layout = sweepers:%d empty-real:%d", batchSweepers, emptyGroupKeys)
	}

	layoutIdx, pageIdx := -1, -1
	for i, key := range uiKeys {
		if key == "__layout_assignment_batch__" {
			layoutIdx = i
		}
		if key == "_pack_sweep_non_default" {
			pageIdx = i
		}
	}
	if layoutIdx == -1 || pageIdx == -1 || layoutIdx > pageIdx {
		t.Fatalf("unexpected UI sweeper order: %v", uiKeys)
	}
}

func TestApplySourceBackedLayoutAssignmentPackBatchFailsClosedOnAdminOverrideAndDeletesStaleOwnedRoute(t *testing.T) {
	batch := newSourceBackedLayoutAssignmentPackBatch()
	want := sourceBackedLayoutAssignmentRow{
		ComponentBusinessKey: "order_rt_routes",
		AssignmentID:         generatedSourceBackedLayoutAssignmentID("salesforce-crm-standard", "order_rt_routes", "Order", "Expansion"),
		AssignmentIDPrefix:   sourceBackedLayoutAssignmentComponentIDPrefix("salesforce-crm-standard", "order_rt_routes", "Order"),
		ObjectAPIName:        "Order",
		RecordTypeName:       "Expansion",
		RecordTypeID:         generatedRecordTypeID("salesforce-crm-standard", "Order", "Expansion"),
		LayoutBusinessKey:    "Order/default",
		LayoutID:             sourceBackedLayoutID("Order", "", true),
		RouteSource:          models.LayoutAssignmentRouteSourceManagedSeed,
		OwnerPack:            "salesforce-crm-standard",
	}
	if err := batch.add([]sourceBackedLayoutAssignmentRow{want}); err != nil {
		t.Fatalf("batch.add: %v", err)
	}

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

	staleID := generatedSourceBackedLayoutAssignmentID("salesforce-crm-standard", "old_route", "Order", "Renewal")
	mock.ExpectBegin()
	mock.ExpectQuery(`SELECT rt\.Id\s+FROM _System_RecordType rt`).
		WithArgs(want.RecordTypeID, "Order", "Expansion", "salesforce-crm-standard").
		WillReturnRows(sqlmock.NewRows([]string{"Id"}).AddRow(want.RecordTypeID))
	mock.ExpectQuery(`SELECT Id, ObjectApiName, COALESCE\(LayoutName, ''\), COALESCE\(Source, ''\), COALESCE\(IsCustomized, 0\), COALESCE\(OwnerPack, ''\)\s+FROM _System_PageLayout\s+WHERE Id = \?`).
		WithArgs(want.LayoutID).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ObjectApiName", "LayoutName", "Source", "IsCustomized", "OwnerPack"}).
			AddRow(want.LayoutID, "Order", "", "managed", 0, "salesforce-crm-standard"))
	mock.ExpectQuery(`SELECT j\.Id, j\.ObjectApiName, rt\.Id, rt\.Name`).
		WithArgs("salesforce-crm-standard", models.LayoutAssignmentRouteSourceManagedSeed).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ObjectApiName", "RecordTypeId", "Name"}).
			AddRow(staleID, "Order", generatedRecordTypeID("salesforce-crm-standard", "Order", "Renewal"), "Renewal"))
	mock.ExpectExec(`DELETE FROM _System_LayoutAssignment\s+WHERE Id = \?`).
		WithArgs(staleID).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectQuery(`SELECT Id, ObjectApiName, LayoutId, COALESCE\(RouteSource, ''\), COALESCE\(OwnerPack, ''\)`).
		WithArgs("Order", want.RecordTypeID).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ObjectApiName", "LayoutId", "RouteSource", "OwnerPack"}).
			AddRow("admin-route", "Order", "admin-layout", models.LayoutAssignmentRouteSourceAdminAuthored, ""))
	err = applySourceBackedLayoutAssignmentPackBatch(context.Background(), db, batch, "salesforce-crm-standard")
	if err == nil {
		t.Fatal("applySourceBackedLayoutAssignmentPackBatch accepted ownerless admin override")
	}
	if !strings.Contains(err.Error(), `fails closed on same-dimension admin override`) {
		t.Fatalf("error = %v, want locked admin override rejection", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestLayoutAssignmentVerifyFailsClosedOnExistingForeignSameDimensionRoute(t *testing.T) {
	batch := newSourceBackedLayoutAssignmentPackBatch()
	component, err := newSourceBackedLayoutAssignmentComponent(
		"salesforce-crm-standard",
		ManifestComponent{
			Kind:          ComponentKindLayoutAssignment,
			BusinessKey:   "order_rt_routes",
			Manageability: ManageabilityLocked,
			DependsOn: []ComponentReference{
				{Kind: ComponentKindRecordType, BusinessKey: "record_types"},
				{Kind: ComponentKindPageLayout, BusinessKey: "Order/default"},
			},
		},
		[]byte(`{"assignments":[{"objectApiName":"Order","recordType":"Expansion"}]}`),
		map[string]sourceBackedRecordTypeTarget{
			recordTypePicklistValueRecordTypeKey("Order", "Expansion"): {
				businessKey:   "record_types",
				objectAPIName: "Order",
				name:          "Expansion",
				expectedID:    generatedRecordTypeID("salesforce-crm-standard", "Order", "Expansion"),
			},
		},
		batch,
	)
	if err != nil {
		t.Fatalf("newSourceBackedLayoutAssignmentComponent: %v", err)
	}
	verifier := component.(componentCustomVerifier)

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

	recordTypeID := generatedRecordTypeID("salesforce-crm-standard", "Order", "Expansion")
	mock.ExpectQuery(`SELECT rt\.Id\s+FROM _System_RecordType rt`).
		WithArgs(recordTypeID, "Order", "Expansion", "salesforce-crm-standard").
		WillReturnRows(sqlmock.NewRows([]string{"Id"}).AddRow(recordTypeID))
	mock.ExpectQuery(`SELECT Id, ObjectApiName, COALESCE\(LayoutName, ''\), COALESCE\(Source, ''\), COALESCE\(IsCustomized, 0\), COALESCE\(OwnerPack, ''\)\s+FROM _System_PageLayout\s+WHERE Id = \?`).
		WithArgs(sourceBackedLayoutID("Order", "", true)).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ObjectApiName", "LayoutName", "Source", "IsCustomized", "OwnerPack"}).
			AddRow(sourceBackedLayoutID("Order", "", true), "Order", "", "managed", 0, "salesforce-crm-standard"))
	mock.ExpectQuery(`SELECT rt\.Id\s+FROM _System_RecordType rt`).
		WithArgs(recordTypeID, "Order", "Expansion", "salesforce-crm-standard").
		WillReturnRows(sqlmock.NewRows([]string{"Id"}).AddRow(recordTypeID))
	mock.ExpectQuery(`SELECT Id, ObjectApiName, COALESCE\(LayoutName, ''\), COALESCE\(Source, ''\), COALESCE\(IsCustomized, 0\), COALESCE\(OwnerPack, ''\)\s+FROM _System_PageLayout\s+WHERE Id = \?`).
		WithArgs(sourceBackedLayoutID("Order", "", true)).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ObjectApiName", "LayoutName", "Source", "IsCustomized", "OwnerPack"}).
			AddRow(sourceBackedLayoutID("Order", "", true), "Order", "", "managed", 0, "salesforce-crm-standard"))
	mock.ExpectQuery(`SELECT j\.Id, j\.ObjectApiName, rt\.Name, j\.LayoutId, COALESCE\(j\.RouteSource, ''\), COALESCE\(j\.OwnerPack, ''\)`).
		WithArgs(recordTypeID, "Order").
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ObjectApiName", "Name", "LayoutId", "RouteSource", "OwnerPack"}).
			AddRow("foreign-id", "Order", "Expansion", sourceBackedLayoutID("Order", "", true), models.LayoutAssignmentRouteSourceManagedSeed, "other-pack"))

	err = verifier.VerifyComponent(context.Background(), VerifyDeps{DB: db})
	if err == nil {
		t.Fatal("VerifyComponent accepted foreign same-dimension route")
	}
	if !strings.Contains(err.Error(), `effective assignment id = "foreign-id"`) {
		t.Fatalf("error = %v, want effective assignment id rejection", err)
	}
}

func TestLayoutAssignmentVerifyFailsClosedOnAdminOverride(t *testing.T) {
	batch := newSourceBackedLayoutAssignmentPackBatch()
	component, err := newSourceBackedLayoutAssignmentComponent(
		"salesforce-crm-standard",
		ManifestComponent{
			Kind:          ComponentKindLayoutAssignment,
			BusinessKey:   "order_rt_routes",
			Manageability: ManageabilityLocked,
			DependsOn: []ComponentReference{
				{Kind: ComponentKindRecordType, BusinessKey: "record_types"},
				{Kind: ComponentKindPageLayout, BusinessKey: "Order/default"},
			},
		},
		[]byte(`{"assignments":[{"objectApiName":"Order","recordType":"Expansion"}]}`),
		map[string]sourceBackedRecordTypeTarget{
			recordTypePicklistValueRecordTypeKey("Order", "Expansion"): {
				businessKey:   "record_types",
				objectAPIName: "Order",
				name:          "Expansion",
				expectedID:    generatedRecordTypeID("salesforce-crm-standard", "Order", "Expansion"),
			},
		},
		batch,
	)
	if err != nil {
		t.Fatalf("newSourceBackedLayoutAssignmentComponent: %v", err)
	}
	verifier := component.(componentCustomVerifier)

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

	recordTypeID := generatedRecordTypeID("salesforce-crm-standard", "Order", "Expansion")
	mock.ExpectQuery(`SELECT rt\.Id\s+FROM _System_RecordType rt`).
		WithArgs(recordTypeID, "Order", "Expansion", "salesforce-crm-standard").
		WillReturnRows(sqlmock.NewRows([]string{"Id"}).AddRow(recordTypeID))
	mock.ExpectQuery(`SELECT Id, ObjectApiName, COALESCE\(LayoutName, ''\), COALESCE\(Source, ''\), COALESCE\(IsCustomized, 0\), COALESCE\(OwnerPack, ''\)\s+FROM _System_PageLayout\s+WHERE Id = \?`).
		WithArgs(sourceBackedLayoutID("Order", "", true)).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ObjectApiName", "LayoutName", "Source", "IsCustomized", "OwnerPack"}).
			AddRow(sourceBackedLayoutID("Order", "", true), "Order", "", "managed", 0, "salesforce-crm-standard"))
	mock.ExpectQuery(`SELECT rt\.Id\s+FROM _System_RecordType rt`).
		WithArgs(recordTypeID, "Order", "Expansion", "salesforce-crm-standard").
		WillReturnRows(sqlmock.NewRows([]string{"Id"}).AddRow(recordTypeID))
	mock.ExpectQuery(`SELECT Id, ObjectApiName, COALESCE\(LayoutName, ''\), COALESCE\(Source, ''\), COALESCE\(IsCustomized, 0\), COALESCE\(OwnerPack, ''\)\s+FROM _System_PageLayout\s+WHERE Id = \?`).
		WithArgs(sourceBackedLayoutID("Order", "", true)).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ObjectApiName", "LayoutName", "Source", "IsCustomized", "OwnerPack"}).
			AddRow(sourceBackedLayoutID("Order", "", true), "Order", "", "managed", 0, "salesforce-crm-standard"))
	mock.ExpectQuery(`SELECT j\.Id, j\.ObjectApiName, rt\.Name, j\.LayoutId, COALESCE\(j\.RouteSource, ''\), COALESCE\(j\.OwnerPack, ''\)`).
		WithArgs(recordTypeID, "Order").
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ObjectApiName", "Name", "LayoutId", "RouteSource", "OwnerPack"}).
			AddRow("admin-route", "Order", "Expansion", sourceBackedLayoutID("Order", "", true), models.LayoutAssignmentRouteSourceAdminAuthored, ""))

	err = verifier.VerifyComponent(context.Background(), VerifyDeps{DB: db})
	if err == nil {
		t.Fatal("VerifyComponent accepted ownerless admin override")
	}
	if !strings.Contains(err.Error(), `fails closed on same-dimension admin override`) {
		t.Fatalf("error = %v, want locked admin override rejection", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestNewSourceBackedLayoutAssignmentComponentThreadsAuthoritativeRecordTypeID(t *testing.T) {
	batch := newSourceBackedLayoutAssignmentPackBatch()
	recordTypeID := generatedRecordTypeID("salesforce-crm-standard", "Order", "Expansion")

	_, err := newSourceBackedLayoutAssignmentComponent(
		"salesforce-crm-standard",
		ManifestComponent{
			Kind:          ComponentKindLayoutAssignment,
			BusinessKey:   "order_rt_routes",
			Manageability: ManageabilityLocked,
			DependsOn: []ComponentReference{
				{Kind: ComponentKindRecordType, BusinessKey: "record_types"},
				{Kind: ComponentKindPageLayout, BusinessKey: "Order/default"},
			},
		},
		[]byte(`{"assignments":[{"objectApiName":"Order","recordType":"Expansion"}]}`),
		map[string]sourceBackedRecordTypeTarget{
			recordTypePicklistValueRecordTypeKey("Order", "Expansion"): {
				businessKey:   "record_types",
				objectAPIName: "Order",
				name:          "Expansion",
				expectedID:    recordTypeID,
			},
		},
		batch,
	)
	if err != nil {
		t.Fatalf("newSourceBackedLayoutAssignmentComponent: %v", err)
	}

	rows := batch.allRows()
	if len(rows) != 1 {
		t.Fatalf("batch routes = %d, want 1", len(rows))
	}
	if rows[0].RecordTypeID != recordTypeID {
		t.Fatalf("declared recordTypeId = %q, want %q", rows[0].RecordTypeID, recordTypeID)
	}
}

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

	mock.ExpectQuery(`SELECT rt\.Id\s+FROM _System_RecordType rt`).
		WithArgs(generatedRecordTypeID("salesforce-crm-standard", "Order", "Different Name"), "Order", "Expansion", "salesforce-crm-standard").
		WillReturnRows(sqlmock.NewRows([]string{"Id"}))
	mock.ExpectQuery(`SELECT rt\.Id\s+FROM _System_RecordType rt`).
		WithArgs("Order", "Expansion", "salesforce-crm-standard").
		WillReturnRows(sqlmock.NewRows([]string{"Id"}).AddRow("rt_live_existing"))

	got, err := resolveLiveRecordTypeIDForLayoutAssignment(
		context.Background(),
		db,
		"salesforce-crm-standard",
		"Order",
		"Expansion",
		generatedRecordTypeID("salesforce-crm-standard", "Order", "Different Name"),
	)
	if err != nil {
		t.Fatalf("resolveLiveRecordTypeIDForLayoutAssignment: %v", err)
	}
	if got != "rt_live_existing" {
		t.Fatalf("resolved record type id = %q, want %q", got, "rt_live_existing")
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	mock.ExpectQuery(`SELECT rt\.Id\s+FROM _System_RecordType rt`).
		WithArgs(generatedRecordTypeID("salesforce-crm-standard", "Order", "Different Name"), "Order", "Expansion", "salesforce-crm-standard").
		WillReturnRows(sqlmock.NewRows([]string{"Id"}))
	mock.ExpectQuery(`SELECT rt\.Id\s+FROM _System_RecordType rt`).
		WithArgs("Order", "Expansion", "salesforce-crm-standard").
		WillReturnRows(sqlmock.NewRows([]string{"Id"}).
			AddRow("rt_live_a").
			AddRow("rt_live_b"))

	_, err = resolveLiveRecordTypeIDForLayoutAssignment(
		context.Background(),
		db,
		"salesforce-crm-standard",
		"Order",
		"Expansion",
		generatedRecordTypeID("salesforce-crm-standard", "Order", "Different Name"),
	)
	if err == nil {
		t.Fatal("resolveLiveRecordTypeIDForLayoutAssignment accepted ambiguous current-authority name match")
	}
	if !strings.Contains(err.Error(), "matches multiple live rows") {
		t.Fatalf("error = %v, want ambiguous current-authority rejection", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestLayoutAssignmentVerifyAcceptsExpectedIDMismatchWhenCurrentAuthorityFallbackIsUnique(t *testing.T) {
	batch := newSourceBackedLayoutAssignmentPackBatch()
	component, err := newSourceBackedLayoutAssignmentComponent(
		"salesforce-crm-standard",
		ManifestComponent{
			Kind:          ComponentKindLayoutAssignment,
			BusinessKey:   "order_rt_routes",
			Manageability: ManageabilityLocked,
			DependsOn: []ComponentReference{
				{Kind: ComponentKindRecordType, BusinessKey: "record_types"},
				{Kind: ComponentKindPageLayout, BusinessKey: "Order/default"},
			},
		},
		[]byte(`{"assignments":[{"objectApiName":"Order","recordType":"Expansion"}]}`),
		map[string]sourceBackedRecordTypeTarget{
			recordTypePicklistValueRecordTypeKey("Order", "Expansion"): {
				businessKey:   "record_types",
				objectAPIName: "Order",
				name:          "Expansion",
				expectedID:    generatedRecordTypeID("salesforce-crm-standard", "Order", "Different Name"),
			},
		},
		batch,
	)
	if err != nil {
		t.Fatalf("newSourceBackedLayoutAssignmentComponent: %v", err)
	}
	verifier := component.(componentCustomVerifier)

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

	mock.ExpectQuery(`SELECT rt\.Id\s+FROM _System_RecordType rt`).
		WithArgs(generatedRecordTypeID("salesforce-crm-standard", "Order", "Different Name"), "Order", "Expansion", "salesforce-crm-standard").
		WillReturnRows(sqlmock.NewRows([]string{"Id"}))
	mock.ExpectQuery(`SELECT rt\.Id\s+FROM _System_RecordType rt`).
		WithArgs("Order", "Expansion", "salesforce-crm-standard").
		WillReturnRows(sqlmock.NewRows([]string{"Id"}).AddRow("rt_live_existing"))
	mock.ExpectQuery(`SELECT Id, ObjectApiName, COALESCE\(LayoutName, ''\), COALESCE\(Source, ''\), COALESCE\(IsCustomized, 0\), COALESCE\(OwnerPack, ''\)\s+FROM _System_PageLayout\s+WHERE Id = \?`).
		WithArgs(sourceBackedLayoutID("Order", "", true)).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ObjectApiName", "LayoutName", "Source", "IsCustomized", "OwnerPack"}).
			AddRow(sourceBackedLayoutID("Order", "", true), "Order", "", "managed", 0, "salesforce-crm-standard"))
	mock.ExpectQuery(`SELECT rt\.Id\s+FROM _System_RecordType rt`).
		WithArgs(generatedRecordTypeID("salesforce-crm-standard", "Order", "Different Name"), "Order", "Expansion", "salesforce-crm-standard").
		WillReturnRows(sqlmock.NewRows([]string{"Id"}))
	mock.ExpectQuery(`SELECT rt\.Id\s+FROM _System_RecordType rt`).
		WithArgs("Order", "Expansion", "salesforce-crm-standard").
		WillReturnRows(sqlmock.NewRows([]string{"Id"}).AddRow("rt_live_existing"))
	mock.ExpectQuery(`SELECT Id, ObjectApiName, COALESCE\(LayoutName, ''\), COALESCE\(Source, ''\), COALESCE\(IsCustomized, 0\), COALESCE\(OwnerPack, ''\)\s+FROM _System_PageLayout\s+WHERE Id = \?`).
		WithArgs(sourceBackedLayoutID("Order", "", true)).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ObjectApiName", "LayoutName", "Source", "IsCustomized", "OwnerPack"}).
			AddRow(sourceBackedLayoutID("Order", "", true), "Order", "", "managed", 0, "salesforce-crm-standard"))
	mock.ExpectQuery(`SELECT j\.Id, j\.ObjectApiName, rt\.Name, j\.LayoutId, COALESCE\(j\.RouteSource, ''\), COALESCE\(j\.OwnerPack, ''\)`).
		WithArgs("rt_live_existing", "Order").
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ObjectApiName", "Name", "LayoutId", "RouteSource", "OwnerPack"}).
			AddRow(
				generatedSourceBackedLayoutAssignmentID("salesforce-crm-standard", "order_rt_routes", "Order", "Expansion"),
				"Order",
				"Expansion",
				sourceBackedLayoutID("Order", "", true),
				models.LayoutAssignmentRouteSourceManagedSeed,
				"salesforce-crm-standard",
			))
	mock.ExpectQuery(`SELECT Id\s+FROM _System_LayoutAssignment`).
		WithArgs("salesforce-crm-standard", "Order", models.LayoutAssignmentRouteSourceManagedSeed, sourceBackedLayoutAssignmentComponentIDPrefix("salesforce-crm-standard", "order_rt_routes", "Order")+`_%`).
		WillReturnRows(sqlmock.NewRows([]string{"Id"}).
			AddRow(generatedSourceBackedLayoutAssignmentID("salesforce-crm-standard", "order_rt_routes", "Order", "Expansion")))

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

func TestApplySourceBackedLayoutAssignmentPackBatchAcceptsUniqueCurrentAuthorityLayoutIDFallback(t *testing.T) {
	batch := newSourceBackedLayoutAssignmentPackBatch()
	want := sourceBackedLayoutAssignmentRow{
		ComponentBusinessKey: "order_expansion_route",
		AssignmentID:         generatedSourceBackedLayoutAssignmentID("salesforce-crm-standard", "order_expansion_route", "Order", "Expansion"),
		AssignmentIDPrefix:   sourceBackedLayoutAssignmentComponentIDPrefix("salesforce-crm-standard", "order_expansion_route", "Order"),
		ObjectAPIName:        "Order",
		RecordTypeName:       "Expansion",
		RecordTypeID:         generatedRecordTypeID("salesforce-crm-standard", "Order", "Expansion"),
		LayoutBusinessKey:    "Order/Sales",
		LayoutID:             sourceBackedLayoutID("Order", "Sales", false),
		RouteSource:          models.LayoutAssignmentRouteSourceManagedSeed,
		OwnerPack:            "salesforce-crm-standard",
	}
	if err := batch.add([]sourceBackedLayoutAssignmentRow{want}); err != nil {
		t.Fatalf("batch.add: %v", err)
	}

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

	liveLayoutID := "layout_live_sales"
	mock.ExpectBegin()
	mock.ExpectQuery(`SELECT rt\.Id\s+FROM _System_RecordType rt`).
		WithArgs(want.RecordTypeID, "Order", "Expansion", "salesforce-crm-standard").
		WillReturnRows(sqlmock.NewRows([]string{"Id"}).AddRow(want.RecordTypeID))
	mock.ExpectQuery(`SELECT Id, ObjectApiName, COALESCE\(LayoutName, ''\), COALESCE\(Source, ''\), COALESCE\(IsCustomized, 0\), COALESCE\(OwnerPack, ''\)\s+FROM _System_PageLayout\s+WHERE Id = \?`).
		WithArgs(want.LayoutID).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ObjectApiName", "LayoutName", "Source", "IsCustomized", "OwnerPack"}))
	mock.ExpectQuery(`SELECT Id, ObjectApiName, COALESCE\(LayoutName, ''\), COALESCE\(Source, ''\), COALESCE\(IsCustomized, 0\), COALESCE\(OwnerPack, ''\)\s+FROM _System_PageLayout\s+WHERE LOWER\(ObjectApiName\) = LOWER\(\?\)\s+AND LOWER\(COALESCE\(LayoutName, ''\)\) = LOWER\(\?\)`).
		WithArgs("Order", "Sales").
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ObjectApiName", "LayoutName", "Source", "IsCustomized", "OwnerPack"}).
			AddRow(liveLayoutID, "Order", "Sales", "managed", 0, "salesforce-crm-standard"))
	mock.ExpectQuery(`SELECT j\.Id, j\.ObjectApiName, rt\.Id, rt\.Name`).
		WithArgs("salesforce-crm-standard", models.LayoutAssignmentRouteSourceManagedSeed).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ObjectApiName", "RecordTypeId", "Name"}))
	mock.ExpectQuery(`SELECT Id, ObjectApiName, LayoutId, COALESCE\(RouteSource, ''\), COALESCE\(OwnerPack, ''\)`).
		WithArgs("Order", want.RecordTypeID).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ObjectApiName", "LayoutId", "RouteSource", "OwnerPack"}))
	mock.ExpectExec(`UPDATE _System_LayoutAssignment\s+SET LayoutId = \?, Specificity = 1, RouteSource = \?, OwnerPack = \?, LastModifiedDate = CURRENT_TIMESTAMP\s+WHERE Id = \?`).
		WithArgs(liveLayoutID, want.RouteSource, want.OwnerPack, want.AssignmentID).
		WillReturnResult(sqlmock.NewResult(0, 0))
	mock.ExpectExec(`INSERT INTO _System_LayoutAssignment`).
		WithArgs(want.AssignmentID, "Order", liveLayoutID, want.RecordTypeID, want.RouteSource, want.OwnerPack).
		WillReturnResult(sqlmock.NewResult(1, 1))
	mock.ExpectCommit()

	if err := applySourceBackedLayoutAssignmentPackBatch(context.Background(), db, batch, "salesforce-crm-standard"); err != nil {
		t.Fatalf("applySourceBackedLayoutAssignmentPackBatch: %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestLayoutAssignmentVerifyAcceptsUniqueCurrentAuthorityLayoutIDFallback(t *testing.T) {
	batch := newSourceBackedLayoutAssignmentPackBatch()
	component, err := newSourceBackedLayoutAssignmentComponent(
		"salesforce-crm-standard",
		ManifestComponent{
			Kind:          ComponentKindLayoutAssignment,
			BusinessKey:   "order_expansion_route",
			Manageability: ManageabilityLocked,
			DependsOn: []ComponentReference{
				{Kind: ComponentKindRecordType, BusinessKey: "record_types"},
				{Kind: ComponentKindPageLayout, BusinessKey: "Order/Sales"},
			},
		},
		[]byte(`{"assignments":[{"objectApiName":"Order","recordType":"Expansion"}]}`),
		map[string]sourceBackedRecordTypeTarget{
			recordTypePicklistValueRecordTypeKey("Order", "Expansion"): {
				businessKey:   "record_types",
				objectAPIName: "Order",
				name:          "Expansion",
				expectedID:    generatedRecordTypeID("salesforce-crm-standard", "Order", "Expansion"),
			},
		},
		batch,
	)
	if err != nil {
		t.Fatalf("newSourceBackedLayoutAssignmentComponent: %v", err)
	}
	verifier := component.(componentCustomVerifier)

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

	recordTypeID := generatedRecordTypeID("salesforce-crm-standard", "Order", "Expansion")
	liveLayoutID := "layout_live_sales"
	canonicalLayoutID := sourceBackedLayoutID("Order", "Sales", false)

	mock.ExpectQuery(`SELECT rt\.Id\s+FROM _System_RecordType rt`).
		WithArgs(recordTypeID, "Order", "Expansion", "salesforce-crm-standard").
		WillReturnRows(sqlmock.NewRows([]string{"Id"}).AddRow(recordTypeID))
	mock.ExpectQuery(`SELECT Id, ObjectApiName, COALESCE\(LayoutName, ''\), COALESCE\(Source, ''\), COALESCE\(IsCustomized, 0\), COALESCE\(OwnerPack, ''\)\s+FROM _System_PageLayout\s+WHERE Id = \?`).
		WithArgs(canonicalLayoutID).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ObjectApiName", "LayoutName", "Source", "IsCustomized", "OwnerPack"}))
	mock.ExpectQuery(`SELECT Id, ObjectApiName, COALESCE\(LayoutName, ''\), COALESCE\(Source, ''\), COALESCE\(IsCustomized, 0\), COALESCE\(OwnerPack, ''\)\s+FROM _System_PageLayout\s+WHERE LOWER\(ObjectApiName\) = LOWER\(\?\)\s+AND LOWER\(COALESCE\(LayoutName, ''\)\) = LOWER\(\?\)`).
		WithArgs("Order", "Sales").
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ObjectApiName", "LayoutName", "Source", "IsCustomized", "OwnerPack"}).
			AddRow(liveLayoutID, "Order", "Sales", "managed", 0, "salesforce-crm-standard"))
	mock.ExpectQuery(`SELECT rt\.Id\s+FROM _System_RecordType rt`).
		WithArgs(recordTypeID, "Order", "Expansion", "salesforce-crm-standard").
		WillReturnRows(sqlmock.NewRows([]string{"Id"}).AddRow(recordTypeID))
	mock.ExpectQuery(`SELECT Id, ObjectApiName, COALESCE\(LayoutName, ''\), COALESCE\(Source, ''\), COALESCE\(IsCustomized, 0\), COALESCE\(OwnerPack, ''\)\s+FROM _System_PageLayout\s+WHERE Id = \?`).
		WithArgs(canonicalLayoutID).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ObjectApiName", "LayoutName", "Source", "IsCustomized", "OwnerPack"}))
	mock.ExpectQuery(`SELECT Id, ObjectApiName, COALESCE\(LayoutName, ''\), COALESCE\(Source, ''\), COALESCE\(IsCustomized, 0\), COALESCE\(OwnerPack, ''\)\s+FROM _System_PageLayout\s+WHERE LOWER\(ObjectApiName\) = LOWER\(\?\)\s+AND LOWER\(COALESCE\(LayoutName, ''\)\) = LOWER\(\?\)`).
		WithArgs("Order", "Sales").
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ObjectApiName", "LayoutName", "Source", "IsCustomized", "OwnerPack"}).
			AddRow(liveLayoutID, "Order", "Sales", "managed", 0, "salesforce-crm-standard"))
	mock.ExpectQuery(`SELECT j\.Id, j\.ObjectApiName, rt\.Name, j\.LayoutId, COALESCE\(j\.RouteSource, ''\), COALESCE\(j\.OwnerPack, ''\)`).
		WithArgs(recordTypeID, "Order").
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ObjectApiName", "Name", "LayoutId", "RouteSource", "OwnerPack"}).
			AddRow(
				generatedSourceBackedLayoutAssignmentID("salesforce-crm-standard", "order_expansion_route", "Order", "Expansion"),
				"Order",
				"Expansion",
				liveLayoutID,
				models.LayoutAssignmentRouteSourceManagedSeed,
				"salesforce-crm-standard",
			))
	mock.ExpectQuery(`SELECT Id\s+FROM _System_LayoutAssignment`).
		WithArgs("salesforce-crm-standard", "Order", models.LayoutAssignmentRouteSourceManagedSeed, sourceBackedLayoutAssignmentComponentIDPrefix("salesforce-crm-standard", "order_expansion_route", "Order")+`_%`).
		WillReturnRows(sqlmock.NewRows([]string{"Id"}).
			AddRow(generatedSourceBackedLayoutAssignmentID("salesforce-crm-standard", "order_expansion_route", "Order", "Expansion")))

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