package analytics

import (
	"context"
	"errors"
	"reflect"
	"testing"
	"time"

	pkgErrors "github.com/nexuscrm/kernel/pkg/errors"
	"github.com/nexuscrm/kernel/pkg/perftiming"
	"github.com/nexuscrm/kernel/pkg/ports"
	domainrls "github.com/nexuscrm/kernel/pkg/rls"
	"github.com/nexuscrm/kernel/pkg/schemautil"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

func TestReadSelectedRootProjectionUsesQueryRLSAndExactColumnClosure(t *testing.T) {
	schema := &models.ObjectMetadata{APIName: "Issue", Fields: []models.FieldMetadata{
		{APIName: constants.FieldID, Type: constants.FieldTypeString, IsSystem: true},
		{APIName: constants.FieldOwnerID, Type: constants.FieldTypeReference, IsSystem: true},
		{APIName: "Estimate", Type: constants.FieldTypeDouble},
		{APIName: "ProjectId", Type: constants.FieldTypeReference},
	}}
	rlsResult := &domainrls.Result{
		ObjectAPIName: "Issue",
		HasOwnerField: true,
		OwnerUserIDs:  []string{"user-1"},
	}
	repo := &selectedRootCapturingQueryRepository{result: &models.QueryResult{
		Done: true,
		Records: []models.SObject{{
			constants.FieldID: "ISS-1",
			"Estimate":        "12345678901234567890.1200",
			"ProjectId":       "PRJ-1",
		}},
	}}
	permissionChecker := &selectedRootPermissionChecker{result: rlsResult}
	service := NewQueryService(repo, nil, permissionChecker, nil)
	observedStages := map[string]bool{}
	ctx := perftiming.WithObserver(context.Background(), perftiming.ObserverFunc(func(observation perftiming.Observation) {
		observedStages[observation.Name] = true
	}))

	got, err := service.ReadSelectedRootProjection(
		ctx, schema, "ISS-1", []string{"Estimate", "ProjectId"},
		&models.UserSession{ID: "user-1"},
	)
	if err != nil {
		t.Fatalf("ReadSelectedRootProjection: %v", err)
	}
	for _, stage := range []string{
		"selected_root.projection_plan", "selected_root.rls_preparation",
		"selected_root.repository_read", "selected_root.storage_hydration",
	} {
		if !observedStages[stage] {
			t.Fatalf("missing performance observation %q: %#v", stage, observedStages)
		}
	}
	wantFields := []string{constants.FieldID, "Estimate", "ProjectId"}
	if !reflect.DeepEqual(repo.visibleFields, wantFields) || !reflect.DeepEqual(repo.request.SelectFields, wantFields) {
		t.Fatalf("repository projection = visible %#v request %#v, want %#v", repo.visibleFields, repo.request.SelectFields, wantFields)
	}
	if repo.rlsResult != rlsResult {
		t.Fatal("repository did not receive the authoritative BuildRLSResult result")
	}
	if permissionChecker.buildRLSCalls != 1 {
		t.Fatalf("BuildRLSResult calls = %d, want one for user-context read", permissionChecker.buildRLSCalls)
	}
	if len(repo.request.Criteria) != 1 || repo.request.Criteria[0].Field != constants.FieldID || repo.request.Criteria[0].Op != "=" || repo.request.Criteria[0].Val != "ISS-1" {
		t.Fatalf("point criterion = %#v", repo.request.Criteria)
	}
	if repo.request.Limit != 1 || !repo.request.SkipTotalSize {
		t.Fatalf("query bounds = limit %d skipTotal %v", repo.request.Limit, repo.request.SkipTotalSize)
	}
	decimal, ok := schemautil.ExactDecimalForField(got, "Estimate")
	if !ok || decimal.String() != "12345678901234567890.1200" {
		t.Fatalf("exact decimal = %#v, %v", decimal, ok)
	}
	if _, exists := got[constants.FieldOwnerID]; exists {
		t.Fatalf("OwnerId leaked from the RLS evaluation closure: %#v", got)
	}
}

func TestReadSelectedRootProjectionRejectsExpiredDeadlineBeforeRLSAndRepository(t *testing.T) {
	schema := &models.ObjectMetadata{APIName: "Issue", Fields: []models.FieldMetadata{{
		APIName: constants.FieldID, Type: constants.FieldTypeString, IsSystem: true,
	}}}
	repo := &selectedRootCapturingQueryRepository{}
	permissionChecker := &selectedRootPermissionChecker{result: &domainrls.Result{Skip: true}}
	service := NewQueryService(repo, nil, permissionChecker, nil)
	ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(-time.Second))
	defer cancel()

	got, err := service.ReadSelectedRootProjection(ctx, schema, "ISS-1", nil, &models.UserSession{ID: "user-1"})
	if got != nil || !errors.Is(err, context.DeadlineExceeded) {
		t.Fatalf("result = %#v, error = %v, want expired deadline", got, err)
	}
	if repo.calls != 0 || permissionChecker.buildRLSCalls != 0 {
		t.Fatalf("expired request reached RLS/repository: RLS calls %d, repository calls %d", permissionChecker.buildRLSCalls, repo.calls)
	}
}

func TestReadSelectedRootProjectionCancellationStopsBeforeRepositoryAtStageBoundaries(t *testing.T) {
	for _, stage := range []string{"selected_root.projection_plan", "selected_root.rls_preparation"} {
		t.Run(stage, func(t *testing.T) {
			schema := &models.ObjectMetadata{APIName: "Issue", Fields: []models.FieldMetadata{{
				APIName: constants.FieldID, Type: constants.FieldTypeString, IsSystem: true,
			}}}
			repo := &selectedRootCapturingQueryRepository{}
			permissionChecker := &selectedRootPermissionChecker{result: &domainrls.Result{Skip: true}}
			service := NewQueryService(repo, nil, permissionChecker, nil)
			ctx, cancel := context.WithCancel(context.Background())
			ctx = perftiming.WithObserver(ctx, perftiming.ObserverFunc(func(observation perftiming.Observation) {
				if observation.Name == stage {
					cancel()
				}
			}))

			got, err := service.ReadSelectedRootProjection(ctx, schema, "ISS-1", nil, &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 repo.calls != 0 {
				t.Fatalf("repository calls = %d after cancellation at %s, want zero", repo.calls, stage)
			}
		})
	}
}

func TestReadSelectedRootProjectionCancellationWinsAtRepositoryBoundary(t *testing.T) {
	schema := &models.ObjectMetadata{APIName: "Issue", Fields: []models.FieldMetadata{
		{APIName: constants.FieldID, Type: constants.FieldTypeString, IsSystem: true},
	}}
	for _, tc := range []struct {
		name   string
		result *models.QueryResult
		err    error
	}{
		{name: "empty-result", result: &models.QueryResult{Done: true}},
		{name: "repository-error", err: pkgErrors.NewInternalError("query execution error", errors.New("storage unavailable"))},
	} {
		t.Run(tc.name, func(t *testing.T) {
			ctx, cancel := context.WithCancel(context.Background())
			repo := &selectedRootCapturingQueryRepository{result: tc.result, err: tc.err, beforeReturn: cancel}
			service := NewQueryService(repo, nil, &selectedRootPermissionChecker{result: &domainrls.Result{Skip: true}}, nil)

			got, err := service.ReadSelectedRootProjection(ctx, schema, "ISS-1", nil, &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 TestReadSelectedRootProjectionCancellationAfterHydrationReturnsNoPartialRecord(t *testing.T) {
	schema := &models.ObjectMetadata{APIName: "Issue", Fields: []models.FieldMetadata{
		{APIName: constants.FieldID, Type: constants.FieldTypeString, IsSystem: true},
		{APIName: "Subject", Type: constants.FieldTypeString},
	}}
	repo := &selectedRootCapturingQueryRepository{result: &models.QueryResult{
		Done: true, Records: []models.SObject{{constants.FieldID: "ISS-1", "Subject": "must-not-escape"}},
	}}
	ctx, cancel := context.WithCancel(context.Background())
	ctx = perftiming.WithObserver(ctx, perftiming.ObserverFunc(func(observation perftiming.Observation) {
		if observation.Name == "selected_root.storage_hydration" {
			cancel()
		}
	}))
	service := NewQueryService(repo, nil, &selectedRootPermissionChecker{result: &domainrls.Result{Skip: true}}, nil)

	got, err := service.ReadSelectedRootProjection(ctx, schema, "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 TestReadSelectedRootProjectionRepositoryFaultRemainsInternal(t *testing.T) {
	schema := &models.ObjectMetadata{APIName: "Issue", Fields: []models.FieldMetadata{{
		APIName: constants.FieldID, Type: constants.FieldTypeString, IsSystem: true,
	}}}
	want := errors.New("driver detail")
	repo := &selectedRootCapturingQueryRepository{err: want}
	service := NewQueryService(repo, nil, &selectedRootPermissionChecker{result: &domainrls.Result{Skip: true}}, nil)

	got, err := service.ReadSelectedRootProjection(context.Background(), schema, "ISS-1", nil, &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, want) {
		t.Fatalf("error = %T %v, want internal condition retaining operator cause", err, err)
	}
}

func TestReadSelectedRootProjectionNeverFallsBackToWildcardWhenFLSOmitsEveryField(t *testing.T) {
	schema := &models.ObjectMetadata{APIName: "Issue", Fields: []models.FieldMetadata{
		{APIName: constants.FieldID, Type: constants.FieldTypeString, IsSystem: true},
		{APIName: "Secret", Type: constants.FieldTypeString},
	}}
	repo := &selectedRootCapturingQueryRepository{result: &models.QueryResult{Done: true, Records: []models.SObject{{constants.FieldID: "ISS-1"}}}}
	service := NewQueryService(repo, nil, &selectedRootPermissionChecker{result: &domainrls.Result{Skip: true}}, nil)

	if _, err := service.ReadSelectedRootProjection(context.Background(), schema, "ISS-1", nil, &models.UserSession{ID: "user-1"}); err != nil {
		t.Fatalf("ReadSelectedRootProjection: %v", err)
	}
	want := []string{constants.FieldID}
	if !reflect.DeepEqual(repo.visibleFields, want) || !reflect.DeepEqual(repo.request.SelectFields, want) {
		t.Fatalf("all-FLS-hidden projection = visible %#v request %#v, want Id-only", repo.visibleFields, repo.request.SelectFields)
	}
}

func TestReadSelectedRootProjectionPreservesEstablishedRLSBypassContexts(t *testing.T) {
	schema := &models.ObjectMetadata{APIName: "Issue", Fields: []models.FieldMetadata{
		{APIName: constants.FieldID, Type: constants.FieldTypeString, IsSystem: true},
		{APIName: "Subject", Type: constants.FieldTypeString},
	}}
	for _, tc := range []struct {
		name string
		ctx  context.Context
	}{
		{
			name: "system-context",
			ctx:  constants.WithSecurityContext(context.Background(), constants.SystemContext, "selected-root RLS bypass test"),
		},
		{
			name: "scoped-internal-read",
			ctx: constants.WithStandardActionInternalAccessScopes(context.Background(), "sa_read_issue", []constants.StandardActionInternalAccessScope{{
				StandardActionID: "sa_read_issue",
				ObjectAPIName:    "Issue",
				Operation:        constants.PermRead,
				EnforceFLS:       true,
			}}),
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			permissionChecker := &selectedRootPermissionChecker{result: &domainrls.Result{DenyAll: true}}
			repo := &selectedRootCapturingQueryRepository{result: &models.QueryResult{
				Done: true, Records: []models.SObject{{constants.FieldID: "ISS-1", "Subject": "visible"}},
			}}
			service := NewQueryService(repo, nil, permissionChecker, nil)

			got, err := service.ReadSelectedRootProjection(tc.ctx, schema, "ISS-1", []string{"Subject"}, nil)
			if err != nil {
				t.Fatalf("ReadSelectedRootProjection: %v", err)
			}
			if permissionChecker.buildRLSCalls != 0 {
				t.Fatalf("BuildRLSResult calls = %d, want zero under established bypass", permissionChecker.buildRLSCalls)
			}
			if repo.rlsResult != nil {
				t.Fatalf("repository RLS = %#v, want nil bypass", repo.rlsResult)
			}
			if got["Subject"] != "visible" {
				t.Fatalf("projection = %#v", got)
			}
		})
	}
}

type selectedRootCapturingQueryRepository struct {
	ports.QueryRepository
	request       models.QueryRequest
	rlsResult     *domainrls.Result
	visibleFields []string
	result        *models.QueryResult
	err           error
	beforeReturn  func()
	calls         int
}

func (r *selectedRootCapturingQueryRepository) Find(_ context.Context, _ *models.ObjectMetadata, request models.QueryRequest, result *domainrls.Result, fields []string) (*models.QueryResult, error) {
	r.calls++
	r.request = request
	r.rlsResult = result
	r.visibleFields = append([]string(nil), fields...)
	if r.beforeReturn != nil {
		r.beforeReturn()
	}
	return r.result, r.err
}

type selectedRootPermissionChecker struct {
	PermissionChecker
	result        *domainrls.Result
	buildRLSCalls int
}

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