package analytics

import (
	"context"
	"errors"

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

// ReadSelectedRootProjection executes the storage/RLS half of a selected-root
// point read. The records service owns object CRUD, request normalization and
// FLS before calling this method. This method owns query-level RLS and the
// projected repository read, so row-policy metadata and scoped/system bypass
// semantics are interpreted by the same readRLSResult path as every other SQL
// query.
//
// selectedFields must contain canonical, readable root fields. Unlike
// QueryWithSpec, this narrow path intentionally performs no lookup-name,
// relationship, child, compound, or virtual-field hydration.
func (qs *QueryService) ReadSelectedRootProjection(
	ctx context.Context,
	schema *models.ObjectMetadata,
	recordID string,
	selectedFields []string,
	user *models.UserSession,
) (models.Record, error) {
	if err := ctx.Err(); err != nil {
		return nil, err
	}
	if qs == nil || qs.repo == nil || qs.permissions == nil || schema == nil {
		return nil, pkgErrors.NewInternalError("selected-root query dependencies are not configured", nil)
	}
	stageStarted := perftiming.StartObservation(ctx)
	projectionFields := make([]string, 0, len(selectedFields)+1)
	projectionFields = append(projectionFields, constants.FieldID)
	for _, field := range selectedFields {
		if field != constants.FieldID {
			projectionFields = append(projectionFields, field)
		}
	}
	perftiming.Observe(ctx, "selected_root.projection_plan", stageStarted)
	if err := ctx.Err(); err != nil {
		return nil, err
	}

	stageStarted = perftiming.StartObservation(ctx)
	rlsResult := qs.readRLSResult(ctx, schema, schema.APIName, user)
	perftiming.Observe(ctx, "selected_root.rls_preparation", stageStarted)
	if err := ctx.Err(); err != nil {
		return nil, err
	}
	stageStarted = perftiming.StartObservation(ctx)
	result, err := qs.repo.Find(ctx, schema, models.QueryRequest{
		ObjectAPIName: schema.APIName,
		Criteria: []models.QueryCriterion{{
			Field: constants.FieldID,
			Op:    "=",
			Val:   recordID,
		}},
		Limit:         1,
		SkipTotalSize: true,
		SelectFields:  projectionFields,
	}, rlsResult, projectionFields)
	perftiming.Observe(ctx, "selected_root.repository_read", stageStarted)
	if err != nil {
		if ctxErr := ctx.Err(); ctxErr != nil {
			return nil, ctxErr
		}
		var internal interface{ InternalCondition() bool }
		if errors.As(err, &internal) && internal.InternalCondition() {
			return nil, err
		}
		return nil, pkgErrors.NewInternalError("selected-root repository read failed", err)
	}
	// Cancellation wins at every asynchronous seam, including the race where
	// the repository observes an empty result just as the caller cancels. Do
	// not turn that race into a stable not-found outcome.
	if err := ctx.Err(); err != nil {
		return nil, err
	}
	if result == nil || len(result.Records) == 0 {
		return nil, nil
	}

	// QueryWithSpec normally performs this metadata-aware conversion before
	// relationship hydration. The selected-root path stops here: it needs the
	// typed root values and exact-decimal carrier, never lookup labels.
	stageStarted = perftiming.StartObservation(ctx)
	record := schemautil.FromStorageRecord(schema, result.Records[0], projectionFields)
	perftiming.Observe(ctx, "selected_root.storage_hydration", stageStarted)
	if err := ctx.Err(); err != nil {
		return nil, err
	}
	return record, nil
}
