package analytics

import (
	"context"

	"github.com/nexuscrm/kernel/pkg/ports"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/nexuscrm/shared/pkg/queryspec"
)

// ActiveFormulaProjectionAdapter adapts the analytics QueryService to the
// narrow records.ActiveFormulaProjectionPort interface. It routes the
// projection read through QueryService.QueryWithSpec, which goes through
// the formula view via QueryRepository.resolveReadTable — so shape A
// (Type=formula) AND shape B (ProjectionConfig) fields are both projected
// by the view substrate without this adapter needing to know view naming.
//
// Wired in service_manager_init.go via:
//
//	ps.SetActiveFormulaProjector(analytics.NewActiveFormulaProjectionAdapter(sm.QuerySvc))
//
// Depends on ports.QueryService (interface) rather than the concrete
// *QueryService so wiring uses the same handle every other consumer uses
// (sm.QuerySvc is the interface variable in service_manager_init.go).
type ActiveFormulaProjectionAdapter struct {
	query ports.QueryService
}

// NewActiveFormulaProjectionAdapter constructs the adapter.
func NewActiveFormulaProjectionAdapter(qs ports.QueryService) *ActiveFormulaProjectionAdapter {
	return &ActiveFormulaProjectionAdapter{query: qs}
}

// QueryActiveProjectionRow fetches the formula-view row for the given record.
// Returns (nil, nil) when the row is absent.
//
// Security context: this adapter does NOT elevate. The caller decides whether
// SystemContext is appropriate. Write-side callers (PersistenceService.Insert/
// Update/BulkInsert/Restore) elevate ctx to SystemContext before invoking the
// projector because the write has already passed user CRUD/FLS gates and the
// trigger payload consumer is SystemContext-internal. Read-side callers
// (RecomputeActiveFormulaProjection from FlowExecutor) pass the real user so
// user-invoked flows do not get a widened FLS bypass.
//
// user is forwarded directly to QueryWithSpec; nil is acceptable on
// SystemContext-elevated paths.
func (a *ActiveFormulaProjectionAdapter) QueryActiveProjectionRow(
	ctx context.Context,
	user *models.UserSession,
	objectAPIName, recordID string,
	includeDeleted bool,
	selectFields []string,
) (models.SObject, error) {
	if a == nil || a.query == nil || objectAPIName == "" || recordID == "" {
		return nil, nil
	}
	fields := append([]string(nil), selectFields...)

	res, err := a.query.QueryWithSpec(ctx, queryspec.QuerySpec{
		ObjectAPIName: objectAPIName,
		SelectFields:  fields,
		Filter:        queryspec.CriteriaPredicate([]queryspec.QueryCriterion{{Field: "Id", Op: "=", Val: recordID}}),
		Limit:         1,
		ScanAll:       includeDeleted,
	}, user)
	if err != nil {
		return nil, err
	}
	if res == nil || len(res.Records) == 0 {
		return nil, nil
	}
	return res.Records[0], nil
}
