package analytics

import (
	"context"
	"reflect"
	"testing"

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

func TestReadReferenceDisplayProjectionBatchesIDsAndUsesExactPhysicalNameClosure(t *testing.T) {
	schema := referenceDisplayPhysicalNameSchema()
	rlsResult := &domainrls.Result{
		ObjectAPIName: "Account",
		HasOwnerField: true,
		OwnerUserIDs:  []string{"user-1"},
	}
	repo := &selectedRootCapturingQueryRepository{result: &models.QueryResult{
		Done: true,
		Records: []models.SObject{
			{constants.FieldID: "001-B", "Name": "Beta"},
			{constants.FieldID: "001-A", "Name": "Alpha"},
		},
	}}
	permissions := &referenceDisplayPermissionChecker{objectReadable: true, fieldReadable: true, rlsResult: rlsResult}
	service := NewQueryService(repo, nil, permissions, nil)

	got, err := service.ReadReferenceDisplayProjection(
		context.Background(), schema,
		[]string{" 001-B ", "001-A", "001-B", "", "   "},
		&models.UserSession{ID: "user-1"},
	)
	if err != nil {
		t.Fatalf("ReadReferenceDisplayProjection: %v", err)
	}
	wantProjection := []string{constants.FieldID, "Name"}
	if !reflect.DeepEqual(repo.request.SelectFields, wantProjection) || !reflect.DeepEqual(repo.visibleFields, wantProjection) {
		t.Fatalf("projection = request %#v visible %#v, want %#v", repo.request.SelectFields, repo.visibleFields, wantProjection)
	}
	if repo.request.Limit != 2 || !repo.request.SkipTotalSize {
		t.Fatalf("query bounds = limit %d skipTotalSize %v, want 2/true", repo.request.Limit, repo.request.SkipTotalSize)
	}
	if len(repo.request.Criteria) != 1 || repo.request.Criteria[0].Field != constants.FieldID || repo.request.Criteria[0].Op != "IN" {
		t.Fatalf("query criterion = %#v", repo.request.Criteria)
	}
	if values, ok := repo.request.Criteria[0].Val.([]interface{}); !ok || !reflect.DeepEqual(values, []interface{}{"001-A", "001-B"}) {
		t.Fatalf("canonical Id set = %#v, want sorted/deduplicated values", repo.request.Criteria[0].Val)
	}
	if repo.rlsResult != rlsResult || permissions.buildRLSCalls != 1 {
		t.Fatalf("RLS = %#v calls %d, want exact authoritative result once", repo.rlsResult, permissions.buildRLSCalls)
	}
	if repo.calls != 1 || permissions.objectChecks != 1 {
		t.Fatalf("calls = repository %d object checks %d, want one bounded read", repo.calls, permissions.objectChecks)
	}
	if len(got) != 2 || got["001-A"]["Name"] != "Alpha" || got["001-B"]["Name"] != "Beta" {
		t.Fatalf("display projection = %#v", got)
	}
	for _, row := range got {
		if _, leaked := row["Secret__c"]; leaked {
			t.Fatalf("full related record leaked through display projection: %#v", row)
		}
	}
}

func TestReadReferenceDisplayProjectionUsesExactUserIdentityClosureDespiteCompetingNameFlags(t *testing.T) {
	schema := &models.ObjectMetadata{APIName: "User", Fields: []models.FieldMetadata{
		{APIName: constants.FieldID, Type: constants.FieldTypeString, IsSystem: true},
		{APIName: "Username", Type: constants.FieldTypeString, IsNameField: true},
		{APIName: "Email", Type: constants.FieldTypeEmail, IsNameField: true},
		{APIName: "Name", Type: constants.FieldTypeString, IsNameField: true, IsVirtual: true, VirtualKind: constants.VirtualFieldCompound, IsCompound: true, ComposedFrom: []string{"FirstName", "LastName"}},
		{APIName: "FirstName", Type: constants.FieldTypeString},
		{APIName: "LastName", Type: constants.FieldTypeString},
	}}
	repo := &selectedRootCapturingQueryRepository{result: &models.QueryResult{Done: true, Records: []models.SObject{
		{constants.FieldID: "005-A", "Username": "ada@example.test", "FirstName": "Ada", "LastName": "Lovelace"},
	}}}
	permissions := &referenceDisplayPermissionChecker{objectReadable: true, fieldReadable: true, rlsResult: &domainrls.Result{Skip: true}}
	service := NewQueryService(repo, nil, permissions, nil)

	got, err := service.ReadReferenceDisplayProjection(context.Background(), schema, []string{"005-A"}, &models.UserSession{ID: "user-1"})
	if err != nil {
		t.Fatalf("ReadReferenceDisplayProjection: %v", err)
	}
	wantProjection := []string{constants.FieldID, "Username", "FirstName", "LastName"}
	if !reflect.DeepEqual(repo.request.SelectFields, wantProjection) || !reflect.DeepEqual(repo.visibleFields, wantProjection) {
		t.Fatalf("User identity projection = request %#v visible %#v, want %#v", repo.request.SelectFields, repo.visibleFields, wantProjection)
	}
	if got["005-A"]["Username"] != "ada@example.test" || got["005-A"]["FirstName"] != "Ada" || got["005-A"]["LastName"] != "Lovelace" {
		t.Fatalf("User identity values = %#v", got)
	}
	if _, leaked := got["005-A"]["Email"]; leaked {
		t.Fatalf("unrelated full-record field leaked: %#v", got)
	}
}

func TestReadReferenceDisplayProjectionFailsClosedBeforeQueryWhenNameClosureIsUnreadable(t *testing.T) {
	schema := &models.ObjectMetadata{APIName: "User", Fields: []models.FieldMetadata{
		{APIName: constants.FieldID, Type: constants.FieldTypeString, IsSystem: true},
		{APIName: "Username", Type: constants.FieldTypeString},
		{APIName: "Name", Type: constants.FieldTypeString, IsNameField: true, IsVirtual: true, VirtualKind: constants.VirtualFieldCompound, IsCompound: true, ComposedFrom: []string{"FirstName", "LastName"}},
		{APIName: "FirstName", Type: constants.FieldTypeString},
		{APIName: "LastName", Type: constants.FieldTypeString},
	}}
	repo := &selectedRootCapturingQueryRepository{}
	permissions := &referenceDisplayPermissionChecker{
		objectReadable: true,
		fieldReadable:  true,
		fieldVisibility: map[string]bool{
			"FirstName": true,
			"LastName":  false,
		},
		rlsResult: &domainrls.Result{Skip: true},
	}
	service := NewQueryService(repo, nil, permissions, nil)

	got, err := service.ReadReferenceDisplayProjection(context.Background(), schema, []string{"005-A"}, &models.UserSession{ID: "user-1"})
	if err != nil {
		t.Fatalf("ReadReferenceDisplayProjection: %v", err)
	}
	if len(got) != 0 || repo.calls != 0 || permissions.buildRLSCalls != 0 {
		t.Fatalf("denied display reached storage: result %#v repository %d RLS %d", got, repo.calls, permissions.buildRLSCalls)
	}
}

func referenceDisplayPhysicalNameSchema() *models.ObjectMetadata {
	return &models.ObjectMetadata{APIName: "Account", Fields: []models.FieldMetadata{
		{APIName: constants.FieldID, Type: constants.FieldTypeString, IsSystem: true},
		{APIName: "Name", Type: constants.FieldTypeString, IsNameField: true},
		{APIName: "Secret__c", Type: constants.FieldTypeString},
	}}
}

type referenceDisplayPermissionChecker struct {
	PermissionChecker
	objectReadable  bool
	fieldReadable   bool
	fieldVisibility map[string]bool
	rlsResult       *domainrls.Result
	objectChecks    int
	buildRLSCalls   int
}

func (p *referenceDisplayPermissionChecker) CheckObjectPermissionWithUser(context.Context, string, string, *models.UserSession) bool {
	p.objectChecks++
	return p.objectReadable
}

func (p *referenceDisplayPermissionChecker) CheckFieldVisibilityWithUser(_ context.Context, _ string, field string, _ *models.UserSession) (bool, error) {
	if allowed, present := p.fieldVisibility[field]; present {
		return allowed, nil
	}
	return p.fieldReadable, nil
}

func (p *referenceDisplayPermissionChecker) IsFieldReadBlocked(context.Context, string, string) bool {
	return false
}

func (p *referenceDisplayPermissionChecker) BuildRLSResult(context.Context, *models.ObjectMetadata, string, *models.UserSession) *domainrls.Result {
	p.buildRLSCalls++
	return p.rlsResult
}
