package persistence

import (
	"context"
	"strings"
	"testing"

	domainrls "github.com/nexuscrm/kernel/pkg/rls"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

type testQueryViewResolver struct {
	resolved string
}

func (r testQueryViewResolver) ResolveTableName(_ context.Context, _ *models.ObjectMetadata) string {
	return r.resolved
}

func testRecordTypeQuerySchema(readSource *models.ObjectReadSourceConfig) *models.ObjectMetadata {
	return &models.ObjectMetadata{
		APIName:    "RecordType",
		ReadSource: readSource,
		Fields: []models.FieldMetadata{
			{APIName: constants.FieldID, Type: constants.FieldTypeString, IsSystem: true},
			{APIName: constants.FieldName, Type: constants.FieldTypeString, IsNameField: true},
			{APIName: constants.FieldIsDeleted, Type: constants.FieldTypeBoolean, IsSystem: true},
		},
	}
}

func TestResolveQueryReadTargetUsesReadSourceTable(t *testing.T) {
	schema := testRecordTypeQuerySchema(&models.ObjectReadSourceConfig{Kind: "physical_table", PhysicalTable: "_System_RecordType"})
	got, err := resolveQueryReadTarget(t.Context(), schema, nil)
	if err != nil {
		t.Fatalf("resolveQueryReadTarget: %v", err)
	}
	if got != "_System_RecordType" {
		t.Fatalf("resolveQueryReadTarget() = %q, want _System_RecordType", got)
	}
}

func TestResolveQueryReadTargetRejectsReadSourceFormulaViewComposition(t *testing.T) {
	schema := testRecordTypeQuerySchema(&models.ObjectReadSourceConfig{Kind: "physical_table", PhysicalTable: "_System_RecordType"})
	_, err := resolveQueryReadTarget(t.Context(), schema, testQueryViewResolver{resolved: "v_recordtype"})
	if err == nil || !strings.Contains(err.Error(), "cannot be composed with formula view routing yet") {
		t.Fatalf("resolveQueryReadTarget() err = %v, want fail-closed composition error", err)
	}
}

func TestResolveQueryReadTargetForRequestRoutesLookupNameProjection(t *testing.T) {
	schema := &models.ObjectMetadata{
		APIName: "Lead",
		Fields: []models.FieldMetadata{
			{APIName: constants.FieldID, Type: constants.FieldTypeString},
			{APIName: "AccountId", Type: constants.FieldTypeReference},
		},
	}
	got, err := resolveQueryReadTargetForRequest(t.Context(), schema,
		testQueryViewResolver{resolved: "v_Lead"},
		models.QueryRequest{ObjectAPIName: "Lead", SelectFields: []string{"AccountId_Name"}},
		[]string{"AccountId_Name"}, []string{constants.FieldID}, nil)
	if err != nil {
		t.Fatalf("resolveQueryReadTargetForRequest: %v", err)
	}
	if got != "v_Lead" {
		t.Fatalf("read target = %q, want v_Lead", got)
	}
}

func TestResolveQueryReadTargetForRequestUsesBaseForMappedRLSDirectPhysicalAlias(t *testing.T) {
	schema := &models.ObjectMetadata{
		APIName: "GenericObject",
		Fields: []models.FieldMetadata{
			{APIName: constants.FieldID, Type: constants.FieldTypeString},
			{APIName: constants.FieldOwnerID, PhysicalName: "owner_id", Type: constants.FieldTypeReference},
		},
	}
	got, err := resolveQueryReadTargetForRequest(t.Context(), schema,
		testQueryViewResolver{resolved: "v_GenericObject"},
		models.QueryRequest{ObjectAPIName: "GenericObject", SelectFields: []string{constants.FieldID}},
		[]string{constants.FieldID}, []string{constants.FieldID},
		&domainrls.Result{HasOwnerField: true, OwnerUserIDs: []string{"005-reader"}})
	if err != nil {
		t.Fatalf("resolveQueryReadTargetForRequest: %v", err)
	}
	if got != "GenericObject" {
		t.Fatalf("read target = %q, want GenericObject", got)
	}
}

func TestPrepareRLSForReadResolvesTargetReadSourceAndDeletedMode(t *testing.T) {
	repo := NewQueryRepository(nil)
	repo.SetViewResolver(testQueryViewResolver{resolved: "v_GenericObject"})
	targetSchema := &models.ObjectMetadata{
		APIName: "GenericObject",
		Fields: []models.FieldMetadata{
			{APIName: constants.FieldID, IsSystem: true},
			{APIName: constants.FieldIsDeleted, PhysicalName: "is_deleted", IsSystem: true},
		},
	}
	rlsResult := &domainrls.Result{
		ObjectAPIName: "HistoryLike",
		TargetRecordFilters: []domainrls.TargetRecordFilter{{
			TargetObjectAPIName: "GenericObject",
			TargetObjectField:   "TargetObjectType",
			TargetRecordField:   "TargetObjectId",
			TargetSchema:        targetSchema,
			TargetRLSResult:     &domainrls.Result{Skip: true, ObjectAPIName: "GenericObject"},
		}},
	}

	prepared, err := repo.prepareRLSForRead(t.Context(), rlsResult, false)
	if err != nil {
		t.Fatalf("prepareRLSForRead: %v", err)
	}
	filter := prepared.TargetRecordFilters[0]
	if filter.TargetReadTable != "GenericObject" || filter.TargetIDColumn != constants.FieldID || filter.TargetIsDeletedColumn != "is_deleted" || filter.TargetIncludeDeleted {
		t.Fatalf("prepared target filter = %#v", filter)
	}
	if sql := domainrls.ToSQLFilter(prepared, "HistoryLike"); !strings.Contains(sql, "`GenericObject`.`is_deleted` = 0") {
		t.Fatalf("target RLS should filter deleted targets:\n%s", sql)
	}

	preparedAll, err := repo.prepareRLSForRead(t.Context(), rlsResult, true)
	if err != nil {
		t.Fatalf("prepareRLSForRead scanAll: %v", err)
	}
	if !preparedAll.TargetRecordFilters[0].TargetIncludeDeleted {
		t.Fatal("queryAll target RLS should include deleted targets")
	}
	if sql := domainrls.ToSQLFilter(preparedAll, "HistoryLike"); strings.Contains(sql, "is_deleted") {
		t.Fatalf("queryAll target RLS should not filter deleted targets:\n%s", sql)
	}
}

func TestPrepareRLSForReadKeepsFormulaCriteriaOnResolvedView(t *testing.T) {
	repo := NewQueryRepository(nil)
	repo.SetViewResolver(testQueryViewResolver{resolved: "v_GenericObject"})
	formulaExpr := "OwnerId"
	active := "active"
	targetSchema := &models.ObjectMetadata{
		APIName: "GenericObject",
		Fields: []models.FieldMetadata{
			{APIName: constants.FieldID, IsSystem: true},
			{APIName: "OwnerId", Type: constants.FieldTypeString},
			{APIName: "ComputedAccess", Type: constants.FieldTypeFormula, Formula: &formulaExpr, FormulaMode: &active},
		},
	}
	prepared, err := repo.prepareRLSForRead(t.Context(), &domainrls.Result{
		ObjectAPIName: "HistoryLike",
		TargetRecordFilters: []domainrls.TargetRecordFilter{{
			TargetObjectAPIName: "GenericObject",
			TargetObjectField:   "TargetObjectType",
			TargetRecordField:   "TargetObjectId",
			TargetSchema:        targetSchema,
			TargetRLSResult: &domainrls.Result{
				ObjectAPIName:        "GenericObject",
				CriteriaExpressions:  []string{`ComputedAccess == "005-reader"`},
				CriteriaSQLFragments: []string{"`ComputedAccess` = '005-reader'"},
			},
		}},
	}, false)
	if err != nil {
		t.Fatalf("prepareRLSForRead: %v", err)
	}
	if got := prepared.TargetRecordFilters[0].TargetReadTable; got != "v_GenericObject" {
		t.Fatalf("TargetReadTable = %q, want formula view", got)
	}
}

func TestPrepareRLSForReadUsesBaseTableForPhysicalCriteria(t *testing.T) {
	repo := NewQueryRepository(nil)
	repo.SetViewResolver(testQueryViewResolver{resolved: "v_GenericObject"})
	formulaExpr := "Region"
	active := "active"
	targetSchema := &models.ObjectMetadata{
		APIName: "GenericObject",
		Fields: []models.FieldMetadata{
			{APIName: constants.FieldID, IsSystem: true},
			{APIName: "Region", Type: constants.FieldTypeString},
			{APIName: "DisplayRegion", Type: constants.FieldTypeFormula, Formula: &formulaExpr, FormulaMode: &active},
		},
	}
	prepared, err := repo.prepareRLSForRead(t.Context(), &domainrls.Result{
		ObjectAPIName: "HistoryLike",
		TargetRecordFilters: []domainrls.TargetRecordFilter{{
			TargetObjectAPIName: "GenericObject",
			TargetObjectField:   "TargetObjectType",
			TargetRecordField:   "TargetObjectId",
			TargetSchema:        targetSchema,
			TargetRLSResult: &domainrls.Result{
				ObjectAPIName:        "GenericObject",
				CriteriaExpressions:  []string{`Region == "NA"`},
				CriteriaSQLFragments: []string{"`Region` = 'NA'"},
			},
		}},
	}, false)
	if err != nil {
		t.Fatalf("prepareRLSForRead: %v", err)
	}
	filter := prepared.TargetRecordFilters[0]
	if filter.TargetReadTable != "GenericObject" {
		t.Fatalf("TargetReadTable = %q, want physical table", filter.TargetReadTable)
	}
	if sql := domainrls.ToSQLFilter(prepared, "HistoryLike"); !strings.Contains(sql, "Region = 'NA'") || strings.Contains(sql, "v_GenericObject") {
		t.Fatalf("physical target RLS was not compiled for the base table:\n%s", sql)
	}
}

func TestResolveRLSReadTargetMapsPhysicalAliasesAndUsesViewForViewOnlyFields(t *testing.T) {
	repo := NewQueryRepository(nil)
	repo.SetViewResolver(testQueryViewResolver{resolved: "v_GenericObject"})
	formulaExpr := "Id"
	innerFormulaExpr := "OwnerId"
	active := "active"

	tests := []struct {
		name   string
		fields []models.FieldMetadata
		result *domainrls.Result
		want   string
	}{
		{
			name: "api-named predicates ignore unrelated display formula",
			fields: []models.FieldMetadata{
				{APIName: constants.FieldID, IsSystem: true},
				{APIName: constants.FieldOwnerID, Type: constants.FieldTypeReference},
				{APIName: "DisplayOnly", Type: constants.FieldTypeFormula, Formula: &formulaExpr, FormulaMode: &active},
			},
			result: &domainrls.Result{HasOwnerField: true, OwnerUserIDs: []string{"005-reader"}},
			want:   "GenericObject",
		},
		{
			name: "nested formula criteria does not demote current table",
			fields: []models.FieldMetadata{
				{APIName: constants.FieldID, IsSystem: true},
				{APIName: "TargetType", Type: constants.FieldTypeString},
				{APIName: "TargetId", Type: constants.FieldTypeReference},
				{APIName: "DisplayOnly", Type: constants.FieldTypeFormula, Formula: &formulaExpr, FormulaMode: &active},
			},
			result: &domainrls.Result{TargetRecordFilters: []domainrls.TargetRecordFilter{{
				TargetObjectField: "TargetType", TargetRecordField: "TargetId",
				Alternatives: []domainrls.TargetRecordFilterAlternative{{
					TargetObjectAPIName: "NestedTarget",
					TargetSchema: &models.ObjectMetadata{APIName: "NestedTarget", Fields: []models.FieldMetadata{
						{APIName: constants.FieldID, IsSystem: true},
						{APIName: constants.FieldOwnerID, Type: constants.FieldTypeReference},
						{APIName: "ComputedAccess", Type: constants.FieldTypeFormula, Formula: &innerFormulaExpr, FormulaMode: &active},
					}},
					TargetRLSResult: &domainrls.Result{
						CriteriaExpressions:  []string{`ComputedAccess == "005-reader"`},
						CriteriaSQLFragments: []string{"`ComputedAccess` = '005-reader'"},
					},
				}},
			}}},
			want: "GenericObject",
		},
		{
			name: "owner visibility alias",
			fields: []models.FieldMetadata{
				{APIName: constants.FieldID, IsSystem: true},
				{APIName: constants.FieldOwnerID, PhysicalName: "owner_id", Type: constants.FieldTypeReference},
				{APIName: "Visibility", PhysicalName: "visibility_state", Type: constants.FieldTypeString},
			},
			result: &domainrls.Result{OwnerVisibility: &domainrls.OwnerVisibilityFilter{
				UserID: "005-reader", OwnerField: constants.FieldOwnerID, VisibilityField: "Visibility",
				OwnerVisibleValues: []string{"Private"}, PublicVisibleValues: []string{"Public"},
			}},
			want: "GenericObject",
		},
		{
			name: "owner visibility view-only field",
			fields: []models.FieldMetadata{
				{APIName: constants.FieldID, IsSystem: true},
				{APIName: "Visibility", Type: constants.FieldTypeFormula, Formula: &formulaExpr, FormulaMode: &active},
			},
			result: &domainrls.Result{OwnerVisibility: &domainrls.OwnerVisibilityFilter{
				VisibilityField: "Visibility", PublicVisibleValues: []string{"Public"},
			}},
			want: "v_GenericObject",
		},
		{
			name: "owner sharing alias",
			fields: []models.FieldMetadata{
				{APIName: constants.FieldID, IsSystem: true},
				{APIName: constants.FieldOwnerID, PhysicalName: "owner_id", Type: constants.FieldTypeReference},
			},
			result: &domainrls.Result{HasOwnerField: true, OwnerUserIDs: []string{"005-reader"}},
			want:   "GenericObject",
		},
		{
			name: "manual-share id alias",
			fields: []models.FieldMetadata{
				{APIName: constants.FieldID, PhysicalName: "record_id", IsSystem: true},
			},
			result: &domainrls.Result{UserID: "005-reader", IncludeManualShares: true},
			want:   "GenericObject",
		},
		{
			name: "allowed ids physical alias is mapped",
			fields: []models.FieldMetadata{
				{APIName: constants.FieldID, IsSystem: true},
				{APIName: "FolderId", PhysicalName: "folder_id", Type: constants.FieldTypeReference},
			},
			result: &domainrls.Result{AllowedIDs: []string{"00l-folder"}, AllowedIDsColumn: "FolderId"},
			want:   "GenericObject",
		},
		{
			name: "allowed ids view-only field",
			fields: []models.FieldMetadata{
				{APIName: constants.FieldID, IsSystem: true},
				{APIName: "ComputedFolderId", Type: constants.FieldTypeFormula, Formula: &formulaExpr, FormulaMode: &active},
			},
			result: &domainrls.Result{AllowedIDs: []string{"00l-folder"}, AllowedIDsColumn: "ComputedFolderId"},
			want:   "v_GenericObject",
		},
		{
			name: "controlled-parent foreign-key alias",
			fields: []models.FieldMetadata{
				{APIName: constants.FieldID, IsSystem: true},
				{APIName: "ParentId", PhysicalName: "parent_id", Type: constants.FieldTypeReference},
			},
			result: &domainrls.Result{ParentFilter: &domainrls.ParentFilter{
				ForeignKeyField: "ParentId", ParentRLSResult: &domainrls.Result{Skip: true},
			}},
			want: "GenericObject",
		},
		{
			name: "polymorphic association aliases",
			fields: []models.FieldMetadata{
				{APIName: constants.FieldID, IsSystem: true},
				{APIName: "TargetType", PhysicalName: "target_type", Type: constants.FieldTypeString},
				{APIName: "TargetId", PhysicalName: "target_id", Type: constants.FieldTypeReference},
			},
			result: &domainrls.Result{TargetRecordFilters: []domainrls.TargetRecordFilter{{
				TargetObjectField: "TargetType", TargetRecordField: "TargetId",
				Alternatives: []domainrls.TargetRecordFilterAlternative{{
					TargetObjectAPIName: "GenericTarget", TargetRLSResult: &domainrls.Result{Skip: true},
				}},
			}}},
			want: "GenericObject",
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			schema := &models.ObjectMetadata{APIName: "GenericObject", Fields: test.fields}
			got, err := repo.resolveRLSReadTarget(t.Context(), schema, test.result)
			if err != nil {
				t.Fatalf("resolveRLSReadTarget: %v", err)
			}
			if got != test.want {
				t.Fatalf("read target = %q, want %q", got, test.want)
			}
		})
	}
}

func TestPrepareRLSForReadMapsTargetColumnsForResolvedView(t *testing.T) {
	repo := NewQueryRepository(nil)
	repo.SetViewResolver(testQueryViewResolver{resolved: "v_GenericObject"})
	formulaExpr := "OwnerId"
	active := "active"
	targetSchema := &models.ObjectMetadata{
		APIName: "GenericObject",
		Fields: []models.FieldMetadata{
			{APIName: constants.FieldID, PhysicalName: "record_id", IsSystem: true},
			{APIName: constants.FieldIsDeleted, PhysicalName: "is_deleted", IsSystem: true},
			{APIName: "OwnerId", Type: constants.FieldTypeReference},
			{APIName: "ComputedAccess", Type: constants.FieldTypeFormula, Formula: &formulaExpr, FormulaMode: &active},
		},
	}
	prepared, err := repo.prepareRLSForRead(t.Context(), &domainrls.Result{
		ObjectAPIName: "HistoryLike",
		TargetRecordFilters: []domainrls.TargetRecordFilter{{
			TargetObjectAPIName: "GenericObject",
			TargetObjectField:   "TargetType",
			TargetRecordField:   "TargetId",
			TargetSchema:        targetSchema,
			TargetRLSResult: &domainrls.Result{
				ObjectAPIName:        "GenericObject",
				CriteriaExpressions:  []string{`ComputedAccess == "005-reader"`},
				CriteriaSQLFragments: []string{"`ComputedAccess` = '005-reader'"},
			},
		}},
	}, false)
	if err != nil {
		t.Fatalf("prepareRLSForRead: %v", err)
	}
	filter := prepared.TargetRecordFilters[0]
	if filter.TargetReadTable != "v_GenericObject" || filter.TargetIDColumn != constants.FieldID || filter.TargetIsDeletedColumn != constants.FieldIsDeleted {
		t.Fatalf("prepared target filter = %#v", filter)
	}
	sql := domainrls.ToSQLFilter(prepared, "HistoryLike")
	if !strings.Contains(sql, "`v_GenericObject`.`Id` = `HistoryLike`.`TargetId`") ||
		!strings.Contains(sql, "`v_GenericObject`.`IsDeleted` = 0") ||
		strings.Contains(sql, "record_id") || strings.Contains(sql, "is_deleted") {
		t.Fatalf("target RLS did not use logical view columns:\n%s", sql)
	}
}

func TestPrepareRLSForReadTargetMapsDirectPhysicalAliases(t *testing.T) {
	schema := &models.ObjectMetadata{
		APIName: "GenericObject",
		Fields: []models.FieldMetadata{
			{APIName: constants.FieldID, PhysicalName: "record_id", IsSystem: true},
			{APIName: constants.FieldOwnerID, PhysicalName: "owner_id", Type: constants.FieldTypeReference},
			{APIName: "ParentId", PhysicalName: "parent_id", Type: constants.FieldTypeReference},
			{APIName: "TargetType", PhysicalName: "target_type", Type: constants.FieldTypeString},
			{APIName: "TargetId", PhysicalName: "target_id", Type: constants.FieldTypeReference},
		},
	}
	prepared, err := prepareRLSForReadTarget(&domainrls.Result{
		ObjectAPIName:       "GenericObject",
		HasOwnerField:       true,
		OwnerUserIDs:        []string{"005-reader"},
		UserID:              "005-reader",
		IncludeManualShares: true,
		ParentFilter: &domainrls.ParentFilter{
			ParentObjectAPIName: "Parent", ForeignKeyField: "ParentId",
			ParentRLSResult: &domainrls.Result{Skip: true},
		},
		TargetRecordFilters: []domainrls.TargetRecordFilter{{
			TargetObjectField: "TargetType", TargetRecordField: "TargetId",
			Alternatives: []domainrls.TargetRecordFilterAlternative{{
				TargetObjectAPIName: "Target", TargetRLSResult: &domainrls.Result{Skip: true},
			}},
		}},
	}, schema, "GenericObject")
	if err != nil {
		t.Fatalf("prepareRLSForReadTarget: %v", err)
	}
	if prepared.OwnerIDColumn != "owner_id" || prepared.RecordIDColumn != "record_id" ||
		prepared.ParentFilter.ForeignKeyField != "parent_id" ||
		prepared.TargetRecordFilters[0].TargetObjectField != "target_type" ||
		prepared.TargetRecordFilters[0].TargetRecordField != "target_id" {
		t.Fatalf("prepared RLS fields = %#v", prepared)
	}
	sql := domainrls.ToSQLFilter(prepared, "GenericObject")
	for _, want := range []string{"`owner_id`", "`GenericObject`.`record_id`", "`parent_id`", "`GenericObject`.`target_type`", "`GenericObject`.`target_id`"} {
		if !strings.Contains(sql, want) {
			t.Fatalf("mapped RLS SQL missing %q:\n%s", want, sql)
		}
	}
}

func TestPrepareRLSForReadUsesBaseTableForPhysicalControlledParent(t *testing.T) {
	repo := NewQueryRepository(nil)
	repo.SetViewResolver(testQueryViewResolver{resolved: "v_Opportunity"})
	parentSchema := &models.ObjectMetadata{
		APIName: "Opportunity",
		Fields: []models.FieldMetadata{
			{APIName: constants.FieldID, IsSystem: true},
			{APIName: constants.FieldOwnerID, Type: constants.FieldTypeReference},
		},
	}
	prepared, err := repo.prepareRLSForRead(t.Context(), &domainrls.Result{
		ObjectAPIName: "OpportunityContactRole",
		ParentFilter: &domainrls.ParentFilter{
			ParentObjectAPIName: "Opportunity",
			ForeignKeyField:     "OpportunityId",
			ParentSchema:        parentSchema,
			ParentRLSResult: &domainrls.Result{
				ObjectAPIName: "Opportunity",
				HasOwnerField: true,
				OwnerUserIDs:  []string{"005-reader"},
			},
		},
	}, false)
	if err != nil {
		t.Fatalf("prepareRLSForRead: %v", err)
	}
	if got := prepared.ParentFilter.ParentReadTable; got != "Opportunity" {
		t.Fatalf("ParentReadTable = %q, want physical table", got)
	}
}

func TestPrepareRLSForReadPreservesEmptyGroupedAssociation(t *testing.T) {
	repo := NewQueryRepository(nil)
	prepared, err := repo.prepareRLSForRead(t.Context(), &domainrls.Result{
		ObjectAPIName: "ActivityLike",
		TargetRecordFilters: []domainrls.TargetRecordFilter{{
			TargetObjectField: "WhatType", TargetRecordField: "WhatId",
			Alternatives: []domainrls.TargetRecordFilterAlternative{},
		}},
	}, false)
	if err != nil {
		t.Fatalf("prepareRLSForRead: %v", err)
	}
	if len(prepared.TargetRecordFilters) != 1 || prepared.TargetRecordFilters[0].Alternatives == nil || len(prepared.TargetRecordFilters[0].Alternatives) != 0 {
		t.Fatalf("prepared target filters = %#v, want explicit empty association group", prepared.TargetRecordFilters)
	}
}

func TestPrepareRLSForReadResolvesControlledParentFormulaReadSource(t *testing.T) {
	repo := NewQueryRepository(nil)
	repo.SetViewResolver(testQueryViewResolver{resolved: "v_Opportunity"})
	formulaExpr := "TEXT(Account.SubUnit__c)"
	active := "active"
	parentSchema := &models.ObjectMetadata{
		APIName: "Opportunity",
		Fields: []models.FieldMetadata{
			{APIName: constants.FieldID, PhysicalName: "opportunity_pk", IsSystem: true},
			{APIName: "Sub_Unit__c", Type: constants.FieldTypeFormula, Formula: &formulaExpr, FormulaMode: &active},
		},
	}
	prepared, err := repo.prepareRLSForRead(t.Context(), &domainrls.Result{
		ObjectAPIName: "OpportunityContactRole",
		ParentFilter: &domainrls.ParentFilter{
			ParentObjectAPIName: "Opportunity", ForeignKeyField: "OpportunityId", ParentSchema: parentSchema,
			ParentRLSResult: &domainrls.Result{
				ObjectAPIName: "Opportunity", HasOwnerField: true, OwnerUserIDs: []string{"005-reader"},
				CriteriaExpressions: []string{`Sub_Unit__c == "Hospitality"`}, CriteriaSQLFragments: []string{`Sub_Unit__c = 'Hospitality'`},
			},
		},
	}, false)
	if err != nil {
		t.Fatalf("prepareRLSForRead: %v", err)
	}
	if got := prepared.ParentFilter.ParentReadTable; got != "v_Opportunity" {
		t.Fatalf("ParentReadTable = %q, want v_Opportunity", got)
	}
	if got := prepared.ParentFilter.ParentIDColumn; got != constants.FieldID {
		t.Fatalf("ParentIDColumn = %q, want Id", got)
	}
	sql := domainrls.ToSQLFilter(prepared, "OpportunityContactRole")
	if !strings.Contains(sql, "`OpportunityId` IN (SELECT `Id` FROM `v_Opportunity`") || strings.Contains(sql, "FROM `Opportunity`") || !strings.Contains(sql, "Sub_Unit__c = 'Hospitality'") {
		t.Fatalf("controlled-parent RLS lost its formula-view source or criterion:\n%s", sql)
	}
}

func TestPrepareRLSForReadTargetMapsAllowedIDColumnToReadTarget(t *testing.T) {
	schema := &models.ObjectMetadata{
		APIName: "Report",
		Fields: []models.FieldMetadata{
			{APIName: constants.FieldID, IsSystem: true},
			{APIName: "FolderId", PhysicalName: "folder_id"},
		},
	}
	rlsResult := &domainrls.Result{ObjectAPIName: "Report", AllowedIDs: []string{"00l-folder-a"}, AllowedIDsColumn: "FolderId"}
	viewPrepared, err := prepareRLSForReadTarget(rlsResult, schema, "v_Report")
	if err != nil {
		t.Fatalf("prepareRLSForReadTarget view: %v", err)
	}
	if sql := domainrls.ToSQLFilter(viewPrepared, "v_Report"); !strings.Contains(sql, "`v_Report`.`FolderId` IN") {
		t.Fatalf("view RLS should use logical FolderId column:\n%s", sql)
	}
	basePrepared, err := prepareRLSForReadTarget(rlsResult, schema, "Report")
	if err != nil {
		t.Fatalf("prepareRLSForReadTarget base: %v", err)
	}
	if sql := domainrls.ToSQLFilter(basePrepared, "Report"); !strings.Contains(sql, "`Report`.`folder_id` IN") {
		t.Fatalf("base RLS should use physical folder_id column:\n%s", sql)
	}
}

func TestFindRejectsMalformedReadSourceBeforeSQL(t *testing.T) {
	repo := NewQueryRepository(nil)
	schema := testRecordTypeQuerySchema(&models.ObjectReadSourceConfig{Kind: "view"})
	if _, err := repo.Find(t.Context(), schema, models.QueryRequest{ObjectAPIName: "RecordType"}, nil, []string{constants.FieldID}); err == nil {
		t.Fatal("Find() = nil err, want malformed readSource failure")
	}
}
