package records

import (
	"context"
	"errors"
	"reflect"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
	"testing"
	"time"

	"github.com/nexuscrm/kernel/pkg/apierr"
	appErrors "github.com/nexuscrm/kernel/pkg/errors"
	"github.com/nexuscrm/kernel/pkg/perftiming"
	"github.com/nexuscrm/kernel/pkg/schemautil"
	"github.com/nexuscrm/metadata"
	"github.com/nexuscrm/permissions"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

func TestReadSelectedRecordProjectsTypedDeterministicRootWithoutFLSLeak(t *testing.T) {
	observedStages := map[string]bool{}
	ctx := perftiming.WithObserver(context.Background(), perftiming.ObserverFunc(func(observation perftiming.Observation) {
		observedStages[observation.Name] = true
	}))
	schema := selectedRootTestSchema()
	record := models.SObject{
		constants.FieldID: "ISS-1",
		"Archived":        false,
		"DueDate":         "2026-07-11",
		"EmptySummary":    "",
		"Estimate":        1.2345678901234567e+19, // legacy carrier is intentionally lossy
		"FormulaFlag":     false,
		"Optional":        nil,
		"ProjectId":       "PRJ-1",
		"RetryCount":      int32(0),
		"RollupFlag":      true,
		"Secret":          "must-not-read",
		"Subject":         "selected-root",
		"UpdatedAt":       "2026-07-11T11:10:11+02:00",
	}
	schemautil.SetExactDecimalForField(record, "Estimate", schemautil.ExactDecimal("12345678901234567890.1200"))
	projection := &selectedRootProjectionStub{record: record}
	service := selectedRootTestService(schema, true, map[string]bool{
		"archived": true, "duedate": true, "emptysummary": true, "estimate": true,
		"formulaflag": true, "optional": true, "projectid": true, "retrycount": true,
		"rollupflag": true, "secret": false,
		"subject": true, "updatedat": true, "declaredabsent": true,
	}, projection)

	got, err := service.ReadSelectedRecord(ctx, "Issue", "ISS-1", []string{
		"updatedat", "Secret", "Estimate", "FormulaFlag", "subject", "RetryCount", "ProjectId",
		"Optional", "DeclaredAbsent", "EmptySummary", "DueDate", "Archived", "SUBJECT",
		"RollupFlag",
	}, &models.UserSession{ID: "user-1", ProfileID: constants.ProfileStandardUser})
	if err != nil {
		t.Fatalf("ReadSelectedRecord: %v", err)
	}
	for _, stage := range []string{
		"selected_root.metadata_security_preparation", "selected_root.selection_normalization",
		"selected_root.fls_preparation", "selected_root.projection_read", "selected_root.typed_projection",
	} {
		if !observedStages[stage] {
			t.Fatalf("missing performance observation %q: %#v", stage, observedStages)
		}
	}
	// The execution projection is canonical bytewise order; DeclaredAbsent is
	// sorted before DueDate. Keep the assertion generated from the same public
	// ordering rule rather than request order.
	wantProjection := []string{"Archived", "DeclaredAbsent", "DueDate", "EmptySummary", "Estimate", "FormulaFlag", "Optional", "ProjectId", "RetryCount", "RollupFlag", "Subject", "UpdatedAt"}
	if !reflect.DeepEqual(projection.fields, wantProjection) {
		t.Fatalf("projected fields = %#v, want %#v", projection.fields, wantProjection)
	}
	if got.RecordID != "ISS-1" {
		t.Fatalf("RecordID = %q", got.RecordID)
	}
	wantNames := []string{"Archived", "DueDate", "EmptySummary", "Estimate", "FormulaFlag", "Optional", "ProjectId", "RetryCount", "RollupFlag", "Subject", "UpdatedAt"}
	gotNames := make([]string, len(got.Fields))
	for i := range got.Fields {
		gotNames[i] = got.Fields[i].Name
	}
	if !reflect.DeepEqual(gotNames, wantNames) {
		t.Fatalf("result order = %#v, want %#v", gotNames, wantNames)
	}
	byName := make(map[string]SelectedRootField, len(got.Fields))
	for _, field := range got.Fields {
		byName[field.Name] = field
	}
	if byName["Archived"].Boolean == nil || *byName["Archived"].Boolean {
		t.Fatalf("false value lost: %#v", byName["Archived"])
	}
	if byName["FormulaFlag"].Boolean == nil || *byName["FormulaFlag"].Boolean {
		t.Fatalf("formula boolean false lost: %#v", byName["FormulaFlag"])
	}
	if byName["RollupFlag"].Boolean == nil || !*byName["RollupFlag"].Boolean {
		t.Fatalf("rollup boolean true lost: %#v", byName["RollupFlag"])
	}
	if byName["RetryCount"].Integer == nil || *byName["RetryCount"].Integer != 0 {
		t.Fatalf("integer zero lost: %#v", byName["RetryCount"])
	}
	if byName["EmptySummary"].String == nil || *byName["EmptySummary"].String != "" {
		t.Fatalf("empty string lost: %#v", byName["EmptySummary"])
	}
	if !byName["Optional"].Null {
		t.Fatalf("explicit null lost: %#v", byName["Optional"])
	}
	if got := byName["Estimate"].Decimal; got == nil || got.Coefficient != "123456789012345678901200" || got.Scale != 4 {
		t.Fatalf("exact decimal = %#v", got)
	}
	if got := byName["UpdatedAt"].DateTime; got == nil || *got != "2026-07-11T09:10:11Z" {
		t.Fatalf("datetime = %#v", got)
	}
	if got := byName["DueDate"].Date; got == nil || *got != "2026-07-11" {
		t.Fatalf("date = %#v", got)
	}
	if got := byName["ProjectId"].Reference; got == nil || *got != "PRJ-1" {
		t.Fatalf("reference = %#v", got)
	}
	if _, exists := byName["Secret"]; exists {
		t.Fatal("FLS-hidden field was returned")
	}
	if _, exists := byName["DeclaredAbsent"]; exists {
		t.Fatal("declared-but-absent field was returned")
	}
}

func TestSelectedRootProjectsIntegerAverageRollupAsDecimal(t *testing.T) {
	returnType := constants.FieldTypeDouble
	metadata := models.FieldMetadata{
		APIName:    "AverageQuantity__c",
		Type:       constants.FieldTypeRollupSummary,
		ReturnType: &returnType,
	}
	schema := &models.ObjectMetadata{APIName: "Parent__c", Fields: []models.FieldMetadata{metadata}}
	record := schemautil.FromStorageRecord(schema, models.SObject{
		"AverageQuantity__c": "1.500000000000000000000000000000",
	}, nil)

	field, err := selectedRootFieldFromValue(metadata, record[metadata.APIName], record)
	if err != nil {
		t.Fatalf("selectedRootFieldFromValue: %v", err)
	}
	if field.Kind != SelectedRootDecimal || field.Decimal == nil || field.Decimal.Coefficient != "1500000000000000000000000000000" || field.Decimal.Scale != 30 {
		t.Fatalf("selected-root average = %#v, want exact decimal 1.5", field)
	}
}

func TestReadSelectedRecordRejectsNonRootUnknownAndVirtualSelectionsBeforeRepositoryAccess(t *testing.T) {
	schema := selectedRootTestSchema()
	schema.Fields = append(schema.Fields, models.FieldMetadata{
		APIName: "Synthetic", Type: constants.FieldTypeString, IsVirtual: true, VirtualKind: "test",
	})
	projection := &selectedRootProjectionStub{}
	service := selectedRootTestService(schema, true, map[string]bool{"subject": true}, projection)
	for _, fields := range [][]string{{""}, {"ProjectId.Name"}, {"NotDeclared"}, {"Synthetic"}} {
		if _, err := service.ReadSelectedRecord(context.Background(), "Issue", "ISS-1", fields, &models.UserSession{ID: "user-1"}); err == nil {
			t.Fatalf("selection %#v succeeded, want invalid request", fields)
		}
	}
	if projection.calls != 0 {
		t.Fatalf("projection calls = %d, want zero", projection.calls)
	}
}

func TestReadSelectedRecordFailsClosedWhenExactDecimalCarrierIsMissing(t *testing.T) {
	schema := selectedRootTestSchema()
	projection := &selectedRootProjectionStub{record: models.SObject{constants.FieldID: "ISS-1", "Estimate": float64(12.5)}}
	service := selectedRootTestService(schema, true, map[string]bool{"estimate": true}, projection)

	_, err := service.ReadSelectedRecord(context.Background(), "Issue", "ISS-1", []string{"Estimate"}, &models.UserSession{ID: "user-1"})
	if err == nil || !strings.Contains(err.Error(), "bound metadata") {
		t.Fatalf("error = %v, want internal metadata/storage mismatch", err)
	}
}

func TestReadSelectedRecordProjectionFaultRemainsInternal(t *testing.T) {
	wantCause := errors.New("driver detail")
	projection := &selectedRootProjectionStub{err: wantCause}
	service := selectedRootTestService(selectedRootTestSchema(), true, map[string]bool{"subject": true}, projection)

	got, err := service.ReadSelectedRecord(context.Background(), "Issue", "ISS-1", []string{"Subject"}, &models.UserSession{ID: "user-1"})
	if got != nil {
		t.Fatalf("result = %#v, want nil", got)
	}
	var internal interface{ InternalCondition() bool }
	if !errors.As(err, &internal) || !internal.InternalCondition() || !errors.Is(err, wantCause) {
		t.Fatalf("error = %T %v, want internal condition retaining operator cause", err, err)
	}
}

func TestReadSelectedRecordMasksMissingAndRLSDeniedIdentically(t *testing.T) {
	schema := selectedRootTestSchema()
	user := &models.UserSession{ID: "user-1"}
	errorsSeen := make([]*apierr.Error, 0, 2)
	for _, label := range []string{"missing", "rls-denied"} {
		t.Run(label, func(t *testing.T) {
			service := selectedRootTestService(schema, true, map[string]bool{"subject": true}, &selectedRootProjectionStub{})
			_, err := service.ReadSelectedRecord(context.Background(), "Issue", "ISS-secret", []string{"Subject"}, user)
			var apiError *apierr.Error
			if !errors.As(err, &apiError) {
				t.Fatalf("error = %T %v, want apierr", err, err)
			}
			errorsSeen = append(errorsSeen, apiError)
		})
	}
	if errorsSeen[0].Status != errorsSeen[1].Status || errorsSeen[0].Code != errorsSeen[1].Code || errorsSeen[0].Message != errorsSeen[1].Message {
		t.Fatalf("missing/denied differ: %#v vs %#v", errorsSeen[0], errorsSeen[1])
	}
}

func TestReadSelectedRecordRejectsForbiddenAndCancellationBeforeRepositoryAccess(t *testing.T) {
	schema := selectedRootTestSchema()
	projection := &selectedRootProjectionStub{record: models.SObject{constants.FieldID: "ISS-1", "Subject": "x"}}
	for _, tc := range []struct {
		name      string
		ctx       context.Context
		grantRead bool
		wantErr   error
	}{
		{name: "forbidden", ctx: context.Background(), grantRead: false},
		{name: "canceled", ctx: canceledSelectedRootContext(), grantRead: true, wantErr: context.Canceled},
		{name: "deadline-exceeded", ctx: expiredSelectedRootContext(), grantRead: true, wantErr: context.DeadlineExceeded},
	} {
		t.Run(tc.name, func(t *testing.T) {
			projection.calls = 0
			service := selectedRootTestService(schema, tc.grantRead, map[string]bool{"subject": true}, projection)
			_, err := service.ReadSelectedRecord(tc.ctx, "Issue", "ISS-1", []string{"Subject"}, &models.UserSession{ID: "user-1"})
			if tc.wantErr != nil && !errors.Is(err, tc.wantErr) {
				t.Fatalf("error = %v, want %v", err, tc.wantErr)
			}
			if tc.wantErr == nil {
				var apiError interface{ HTTPStatus() int }
				if !errors.As(err, &apiError) || apiError.HTTPStatus() != 403 {
					t.Fatalf("error = %T %v, want forbidden", err, err)
				}
			}
			if projection.calls != 0 {
				t.Fatalf("projection calls = %d, want zero", projection.calls)
			}
		})
	}
}

func TestReadSelectedRecordPropagatesCancellationDuringExecution(t *testing.T) {
	schema := selectedRootTestSchema()
	projection := &selectedRootProjectionStub{waitForCancellation: true}
	service := selectedRootTestService(schema, true, map[string]bool{"subject": true}, projection)
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
	defer cancel()

	_, err := service.ReadSelectedRecord(ctx, "Issue", "ISS-1", []string{"Subject"}, &models.UserSession{ID: "user-1"})
	if !errors.Is(err, context.DeadlineExceeded) {
		t.Fatalf("error = %v, want deadline exceeded", err)
	}
}

func TestReadSelectedRecordCancellationStopsBeforeProjectionAtStageBoundaries(t *testing.T) {
	for _, stage := range []string{
		"selected_root.metadata_security_preparation",
		"selected_root.selection_normalization",
		"selected_root.fls_preparation",
	} {
		t.Run(stage, func(t *testing.T) {
			projection := &selectedRootProjectionStub{record: models.SObject{constants.FieldID: "ISS-1", "Subject": "must-not-read"}}
			service := selectedRootTestService(selectedRootTestSchema(), true, map[string]bool{"subject": true}, projection)
			ctx, cancel := context.WithCancel(context.Background())
			ctx = perftiming.WithObserver(ctx, perftiming.ObserverFunc(func(observation perftiming.Observation) {
				if observation.Name == stage {
					cancel()
				}
			}))

			got, err := service.ReadSelectedRecord(ctx, "Issue", "ISS-1", []string{"Subject"}, &models.UserSession{ID: "user-1"})
			if got != nil || !errors.Is(err, context.Canceled) {
				t.Fatalf("result = %#v, error = %v, want canceled with no result", got, err)
			}
			if projection.calls != 0 {
				t.Fatalf("projection calls = %d after cancellation at %s, want zero", projection.calls, stage)
			}
		})
	}
}

func TestReadSelectedRecordCancellationWinsAtProjectionBoundary(t *testing.T) {
	schema := selectedRootTestSchema()
	for _, tc := range []struct {
		name   string
		record models.SObject
		err    error
	}{
		{name: "empty-result"},
		{name: "projection-error", err: appErrors.NewInternalError("query execution error", errors.New("storage unavailable"))},
		{name: "record-result", record: models.SObject{constants.FieldID: "ISS-1", "Subject": "must-not-escape"}},
	} {
		t.Run(tc.name, func(t *testing.T) {
			ctx, cancel := context.WithCancel(context.Background())
			projection := &selectedRootProjectionStub{record: tc.record, err: tc.err, beforeReturn: cancel}
			service := selectedRootTestService(schema, true, map[string]bool{"subject": true}, projection)

			got, err := service.ReadSelectedRecord(ctx, "Issue", "ISS-1", []string{"Subject"}, &models.UserSession{ID: "user-1"})
			if got != nil || !errors.Is(err, context.Canceled) {
				t.Fatalf("result = %#v, error = %v, want canceled with no result", got, err)
			}
		})
	}
}

func TestReadSelectedRecordCancellationAfterTypedProjectionReturnsNoPartialRecord(t *testing.T) {
	schema := selectedRootTestSchema()
	projection := &selectedRootProjectionStub{record: models.SObject{constants.FieldID: "ISS-1", "Subject": "must-not-escape"}}
	service := selectedRootTestService(schema, true, map[string]bool{"subject": true}, projection)
	ctx, cancel := context.WithCancel(context.Background())
	ctx = perftiming.WithObserver(ctx, perftiming.ObserverFunc(func(observation perftiming.Observation) {
		if observation.Name == "selected_root.typed_projection" {
			cancel()
		}
	}))

	got, err := service.ReadSelectedRecord(ctx, "Issue", "ISS-1", []string{"Subject"}, &models.UserSession{ID: "user-1"})
	if got != nil || !errors.Is(err, context.Canceled) {
		t.Fatalf("result = %#v, error = %v, want canceled with no partial record", got, err)
	}
}

func TestReadSelectedRecordConcurrentCancellationSettlesEveryInFlightCall(t *testing.T) {
	const callers = 64
	schema := selectedRootTestSchema()
	projection := &selectedRootCancellationProjection{entered: make(chan struct{}, callers)}
	service := selectedRootTestService(schema, true, map[string]bool{"subject": true}, projection)
	contexts := make([]context.Context, callers)
	cancels := make([]context.CancelFunc, callers)
	for i := range contexts {
		contexts[i], cancels[i] = context.WithCancel(context.Background())
	}

	var wg sync.WaitGroup
	errs := make(chan error, callers)
	for i := range contexts {
		wg.Add(1)
		go func(ctx context.Context) {
			defer wg.Done()
			_, err := service.ReadSelectedRecord(ctx, "Issue", "ISS-1", []string{"Subject"}, &models.UserSession{ID: "user-1"})
			errs <- err
		}(contexts[i])
	}
	for range callers {
		select {
		case <-projection.entered:
		case <-time.After(2 * time.Second):
			t.Fatal("timed out waiting for selected-root calls to reach projection")
		}
	}
	for _, cancel := range cancels {
		cancel()
	}
	done := make(chan struct{})
	go func() {
		wg.Wait()
		close(done)
	}()
	select {
	case <-done:
	case <-time.After(2 * time.Second):
		t.Fatal("canceled selected-root calls did not settle")
	}
	close(errs)
	for err := range errs {
		if !errors.Is(err, context.Canceled) {
			t.Fatalf("error = %v, want canceled", err)
		}
	}
	if got := projection.inFlight.Load(); got != 0 {
		t.Fatalf("in-flight projection calls = %d, want zero", got)
	}
	if got := projection.calls.Load(); got != callers {
		t.Fatalf("projection calls = %d, want %d", got, callers)
	}
}

func TestReadSelectedRecordDoesNotRetainProjectionPayloads(t *testing.T) {
	service := selectedRootTestService(
		selectedRootTestSchema(),
		true,
		map[string]bool{"subject": true},
		selectedRootLargePayloadProjection{bytes: 1 << 20},
	)
	runtime.GC()
	var before runtime.MemStats
	runtime.ReadMemStats(&before)
	exerciseSelectedRootLargePayloads(t, service, 64)
	runtime.GC()
	runtime.GC()
	var after runtime.MemStats
	runtime.ReadMemStats(&after)
	const retainedAllowance = 8 << 20
	if after.HeapAlloc > before.HeapAlloc+retainedAllowance {
		t.Fatalf("heap retained %d bytes after 64 MiB of completed reads, allowance %d", after.HeapAlloc-before.HeapAlloc, retainedAllowance)
	}
}

func exerciseSelectedRootLargePayloads(t *testing.T, service *ContractService, calls int) {
	t.Helper()
	for range calls {
		got, err := service.ReadSelectedRecord(context.Background(), "Issue", "ISS-1", []string{"Subject"}, &models.UserSession{ID: "user-1"})
		if err != nil || got == nil || len(got.Fields) != 1 {
			t.Fatalf("large-payload read = %#v, %v", got, err)
		}
	}
}

func selectedRootTestSchema() *models.ObjectMetadata {
	booleanType := constants.FieldTypeBoolean
	return &models.ObjectMetadata{APIName: "Issue", SharingModel: constants.SharingModelPrivate, Fields: []models.FieldMetadata{
		{APIName: constants.FieldID, Type: constants.FieldTypeString, IsSystem: true},
		{APIName: constants.FieldOwnerID, Type: constants.FieldTypeReference, IsSystem: true},
		{APIName: "Archived", Type: constants.FieldTypeBoolean},
		{APIName: "DeclaredAbsent", Type: constants.FieldTypeString},
		{APIName: "DueDate", Type: constants.FieldTypeDate},
		{APIName: "EmptySummary", Type: constants.FieldTypeString},
		{APIName: "Estimate", Type: constants.FieldTypeDouble},
		{APIName: "FormulaFlag", Type: constants.FieldTypeFormula, ReturnType: &booleanType},
		{APIName: "Optional", Type: constants.FieldTypeString},
		{APIName: "ProjectId", Type: constants.FieldTypeReference, ReferenceTo: []string{"Project"}},
		{APIName: "RetryCount", Type: constants.SchemaFieldType("integer")},
		{APIName: "RollupFlag", Type: constants.FieldTypeRollupSummary, ReturnType: &booleanType},
		{APIName: "Secret", Type: constants.FieldTypeString},
		{APIName: "Subject", Type: constants.FieldTypeString},
		{APIName: "UpdatedAt", Type: constants.FieldTypeDatetime},
	}}
}

func selectedRootTestService(schema *models.ObjectMetadata, grantRead bool, fieldRead map[string]bool, projection SelectedRootProjectionReader) *ContractService {
	metadataService := metadata.NewMetadataService(&stubMetaRepoRMR{schema: schema}, nil, nil)
	permissionService := permissions.NewPermissionService(&stubPermRepoRMR{grantRead: grantRead, fieldRead: fieldRead}, metadataService, nil)
	persistenceService := &PersistenceService{metadata: metadataService, permissions: permissionService}
	persistenceService.SetSelectedRootProjectionReader(projection)
	return NewContractService(persistenceService)
}

type selectedRootProjectionStub struct {
	record              models.SObject
	fields              []string
	calls               int
	waitForCancellation bool
	err                 error
	beforeReturn        func()
}

func (s *selectedRootProjectionStub) ReadSelectedRootProjection(ctx context.Context, _ *models.ObjectMetadata, _ string, fields []string, _ *models.UserSession) (models.SObject, error) {
	s.calls++
	s.fields = append([]string(nil), fields...)
	if s.waitForCancellation {
		<-ctx.Done()
		return nil, ctx.Err()
	}
	if s.beforeReturn != nil {
		s.beforeReturn()
	}
	return s.record, s.err
}

type selectedRootCancellationProjection struct {
	entered  chan struct{}
	calls    atomic.Int64
	inFlight atomic.Int64
}

type selectedRootLargePayloadProjection struct {
	bytes int
}

func (s selectedRootLargePayloadProjection) ReadSelectedRootProjection(_ context.Context, _ *models.ObjectMetadata, _ string, _ []string, _ *models.UserSession) (models.SObject, error) {
	return models.SObject{constants.FieldID: "ISS-1", "Subject": strings.Repeat("x", s.bytes)}, nil
}

func (s *selectedRootCancellationProjection) ReadSelectedRootProjection(ctx context.Context, _ *models.ObjectMetadata, _ string, _ []string, _ *models.UserSession) (models.SObject, error) {
	s.calls.Add(1)
	s.inFlight.Add(1)
	defer s.inFlight.Add(-1)
	s.entered <- struct{}{}
	<-ctx.Done()
	return nil, ctx.Err()
}

func canceledSelectedRootContext() context.Context {
	ctx, cancel := context.WithCancel(context.Background())
	cancel()
	return ctx
}

func expiredSelectedRootContext() context.Context {
	ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(-time.Second))
	cancel()
	return ctx
}
